diff --git a/.CMake/alg_support.cmake b/.CMake/alg_support.cmake index bdd4d94ce..9fdf37cb1 100644 --- a/.CMake/alg_support.cmake +++ b/.CMake/alg_support.cmake @@ -169,6 +169,26 @@ cmake_dependent_option(OQS_ENABLE_SIG_mayo_1 "" ON "OQS_ENABLE_SIG_MAYO" OFF) cmake_dependent_option(OQS_ENABLE_SIG_mayo_2 "" ON "OQS_ENABLE_SIG_MAYO" OFF) cmake_dependent_option(OQS_ENABLE_SIG_mayo_3 "" ON "OQS_ENABLE_SIG_MAYO" OFF) cmake_dependent_option(OQS_ENABLE_SIG_mayo_5 "" ON "OQS_ENABLE_SIG_MAYO" OFF) + +option(OQS_ENABLE_SIG_CROSS "Enable cross algorithm family" ON) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_balanced "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_fast "" ON "OQS_ENABLE_SIG_CROSS" OFF) +cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_small "" ON "OQS_ENABLE_SIG_CROSS" OFF) ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_ADD_ENABLE_BY_ALG_END ##### OQS_COPY_FROM_LIBJADE_FRAGMENT_ADD_ENABLE_BY_ALG_START @@ -197,7 +217,7 @@ elseif (${OQS_ALGS_ENABLED} STREQUAL "STD") elseif(${OQS_ALGS_ENABLED} STREQUAL "NIST_R4") filter_algs("KEM_classic_mceliece_348864;KEM_classic_mceliece_348864f;KEM_classic_mceliece_460896;KEM_classic_mceliece_460896f;KEM_classic_mceliece_6688128;KEM_classic_mceliece_6688128f;KEM_classic_mceliece_6960119;KEM_classic_mceliece_6960119f;KEM_classic_mceliece_8192128;KEM_classic_mceliece_8192128f;KEM_hqc_128;KEM_hqc_192;KEM_hqc_256;KEM_bike_l1;KEM_bike_l3;KEM_bike_l5") elseif(${OQS_ALGS_ENABLED} STREQUAL "NIST_SIG_ONRAMP") - filter_algs("SIG_mayo_1;SIG_mayo_2;SIG_mayo_3;SIG_mayo_5") + filter_algs("SIG_mayo_1;SIG_mayo_2;SIG_mayo_3;SIG_mayo_5;SIG_cross_rsdp_128_balanced;SIG_cross_rsdp_128_fast;SIG_cross_rsdp_128_small;SIG_cross_rsdp_192_balanced;SIG_cross_rsdp_192_fast;SIG_cross_rsdp_192_small;SIG_cross_rsdp_256_balanced;SIG_cross_rsdp_256_fast;SIG_cross_rsdp_256_small;SIG_cross_rsdpg_128_balanced;SIG_cross_rsdpg_128_fast;SIG_cross_rsdpg_128_small;SIG_cross_rsdpg_192_balanced;SIG_cross_rsdpg_192_fast;SIG_cross_rsdpg_192_small;SIG_cross_rsdpg_256_balanced;SIG_cross_rsdpg_256_fast;SIG_cross_rsdpg_256_small") else() message(STATUS "Alg enablement unchanged") endif() @@ -531,6 +551,79 @@ if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) endif() endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_128_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_128_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_128_small" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_192_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_192_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_192_small" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_256_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_256_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdp_256_small" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_128_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_128_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_128_small" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_192_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_192_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_192_small" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_256_balanced" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_256_fast" OFF) +endif() + +if(OQS_DIST_X86_64_BUILD OR (OQS_USE_AVX2_INSTRUCTIONS)) + cmake_dependent_option(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2 "" ON "OQS_ENABLE_SIG_cross_rsdpg_256_small" OFF) +endif() + ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_ADD_ENABLE_BY_ALG_CONDITIONAL_END ##### OQS_COPY_FROM_LIBJADE_FRAGMENT_ADD_ENABLE_BY_ALG_CONDITIONAL_START diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 724402e7a..0530ff1c1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,9 +2,10 @@ * @dstebila /.circleci @baentsch -/scripts/copy_from_upstream @baentsch @bhess +/scripts/copy_from_upstream @baentsch @bhess @alexrow /src/common @dstebila /src/kem/bike @crockeea /src/kem/frodokem @dstebila /src/kem/kyber @jschanck @bhess +/src/sig/cross @alexrow /src/sig/dilithium @bhess diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index 35bb8deb6..0f5b5dbe4 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -157,10 +157,10 @@ jobs: container: openquantumsafe/ci-ubuntu-focal-x86_64:latest CMAKE_ARGS: -DCMAKE_C_COMPILER=gcc-7 -DOQS_DIST_BUILD=OFF -DOQS_USE_OPENSSL=OFF -DBUILD_SHARED_LIBS=ON PYTEST_ARGS: --ignore=tests/test_namespace.py --ignore=tests/test_leaks.py --ignore=tests/test_kat_all.py - - name: focal-clang15 + - name: jammy-clang runner: ubuntu-latest - container: openquantumsafe/ci-ubuntu-focal-x86_64:latest - CMAKE_ARGS: -DOQS_STRICT_WARNINGS=ON -DCMAKE_C_COMPILER=clang-15 + container: openquantumsafe/ci-ubuntu-jammy:latest + CMAKE_ARGS: -DOQS_STRICT_WARNINGS=ON -DCMAKE_C_COMPILER=clang PYTEST_ARGS: --ignore=tests/test_kat_all.py - name: jammy-std-openssl3 runner: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d897e664..6973fbc5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,9 @@ endif() if(OQS_ENABLE_SIG_MAYO) set(PUBLIC_HEADERS ${PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/src/sig/mayo/sig_mayo.h) endif() +if(OQS_ENABLE_SIG_CROSS) + set(PUBLIC_HEADERS ${PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/src/sig/cross/sig_cross.h) +endif() ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_INCLUDE_HEADERS_END if(OQS_ENABLE_SIG_STFL_XMSS) set(PUBLIC_HEADERS ${PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/src/sig_stfl/xmss/sig_stfl_xmss.h) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 83d9337ca..596b30520 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -35,5 +35,8 @@ Sebastian Verschoor (University of Waterloo) Thom Wiggers (Radboud University) Dindyal Jeevesh Rishi (University of Mauritius / cyberstorm.mu) Duc Tri Nguyen +Marco Gianvecchio (Politecnico di Milano) +Alessandro Barenghi (Politecnico di Milano) +Gerardo Pelosi (Politecnico di Milano) See additional contributors at https://github.com/open-quantum-safe/liboqs/graphs/contributors diff --git a/README.md b/README.md index db67a3115..ecc62daee 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ All names other than `ML-KEM` and `ML-DSA` are subject to change. `liboqs` makes #### Signature schemes +- **CROSS**: cross-rsdp-128-balanced, cross-rsdp-128-fast, cross-rsdp-128-small†, cross-rsdp-192-balanced, cross-rsdp-192-fast, cross-rsdp-192-small†, cross-rsdp-256-balanced†, cross-rsdp-256-fast, cross-rsdp-256-small†, cross-rsdpg-128-balanced, cross-rsdpg-128-fast, cross-rsdpg-128-small, cross-rsdpg-192-balanced, cross-rsdpg-192-fast, cross-rsdpg-192-small†, cross-rsdpg-256-balanced, cross-rsdpg-256-fast, cross-rsdpg-256-small† - **CRYSTALS-Dilithium**: Dilithium2, Dilithium3, Dilithium5 - **Falcon**: Falcon-512, Falcon-1024, Falcon-padded-512, Falcon-padded-1024 - **MAYO**: MAYO-1, MAYO-2, MAYO-3, MAYO-5† diff --git a/docs/algorithms/sig/cross.md b/docs/algorithms/sig/cross.md new file mode 100644 index 000000000..91de3bb82 --- /dev/null +++ b/docs/algorithms/sig/cross.md @@ -0,0 +1,203 @@ +# CROSS + +- **Algorithm type**: Digital signature scheme. +- **Main cryptographic assumption**: hardness of the restricted syndrome decoding problem for random linear codes on a finite field. +- **Principal submitters**: Marco Baldi, Alessandro Barenghi, Sebastian Bitzer, Patrick Karl, Felice Manganiello, Alessio Pavoni, Gerardo Pelosi, Paolo Santini, Jonas Schupp, Freeman Slaughter, Antonia Wachter-Zeh, Violetta Weger. +- **Auxiliary submitters**: Marco Gianvecchio. +- **Authors' website**: https://www.cross-crypto.com/ +- **Specification version**: 1.2 + Keccak_x4 + PQClean fixes. +- **Primary Source**: + - **Source**: https://github.com/rtjk/CROSS-PQClean/commit/577d7c761c684637923c8648644cf2f4d7b41954 + - **Implementation license (SPDX-Identifier)**: CC0-1.0 + + +## Parameter set summary + +| Parameter set | Parameter set alias | Security model | Claimed NIST Level | Public key size (bytes) | Secret key size (bytes) | Signature size (bytes) | +|:------------------------:|:----------------------|:-----------------|---------------------:|--------------------------:|--------------------------:|-------------------------:| +| cross-rsdp-128-balanced | NA | EUF-CMA | 1 | 77 | 32 | 12912 | +| cross-rsdp-128-fast | NA | EUF-CMA | 1 | 77 | 32 | 19152 | +| cross-rsdp-128-small | NA | EUF-CMA | 1 | 77 | 32 | 10080 | +| cross-rsdp-192-balanced | NA | EUF-CMA | 3 | 115 | 48 | 28222 | +| cross-rsdp-192-fast | NA | EUF-CMA | 3 | 115 | 48 | 42682 | +| cross-rsdp-192-small | NA | EUF-CMA | 3 | 115 | 48 | 23642 | +| cross-rsdp-256-balanced | NA | EUF-CMA | 5 | 153 | 64 | 51056 | +| cross-rsdp-256-fast | NA | EUF-CMA | 5 | 153 | 64 | 76298 | +| cross-rsdp-256-small | NA | EUF-CMA | 5 | 153 | 64 | 43592 | +| cross-rsdpg-128-balanced | NA | EUF-CMA | 1 | 54 | 32 | 9236 | +| cross-rsdpg-128-fast | NA | EUF-CMA | 1 | 54 | 32 | 12472 | +| cross-rsdpg-128-small | NA | EUF-CMA | 1 | 54 | 32 | 7956 | +| cross-rsdpg-192-balanced | NA | EUF-CMA | 3 | 83 | 48 | 23380 | +| cross-rsdpg-192-fast | NA | EUF-CMA | 3 | 83 | 48 | 27404 | +| cross-rsdpg-192-small | NA | EUF-CMA | 3 | 83 | 48 | 18188 | +| cross-rsdpg-256-balanced | NA | EUF-CMA | 5 | 106 | 64 | 40134 | +| cross-rsdpg-256-fast | NA | EUF-CMA | 5 | 106 | 64 | 48938 | +| cross-rsdpg-256-small | NA | EUF-CMA | 5 | 106 | 64 | 32742 | + +## cross-rsdp-128-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage?‡ | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:----------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **No**. + + ‡For an explanation of what this denotes, consult the [Explanation of Terms](#explanation-of-terms) section at the end of this file. + +## cross-rsdp-128-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-128-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-192-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-192-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-192-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-256-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-256-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdp-256-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-128-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-128-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-128-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-192-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-192-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-192-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-256-balanced implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-256-fast implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | False | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | False | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## cross-rsdpg-256-small implementation characteristics + +| Implementation source | Identifier in upstream | Supported architecture(s) | Supported operating system(s) | CPU extension(s) used | No branching-on-secrets claimed? | No branching-on-secrets checked by valgrind? | Large stack usage? | +|:---------------------------------:|:-------------------------|:----------------------------|:--------------------------------|:------------------------|:-----------------------------------|:-----------------------------------------------|:---------------------| +| [Primary Source](#primary-source) | clean | All | All | None | True | True | True | +| [Primary Source](#primary-source) | avx2 | x86\_64 | All | AVX2 | True | True | True | + +Are implementations chosen based on runtime CPU feature detection? **Yes**. + +## Explanation of Terms + +- **Large Stack Usage**: Implementations identified as having such may cause failures when running in threads or in constrained environments. \ No newline at end of file diff --git a/docs/algorithms/sig/cross.yml b/docs/algorithms/sig/cross.yml new file mode 100644 index 000000000..616da6602 --- /dev/null +++ b/docs/algorithms/sig/cross.yml @@ -0,0 +1,530 @@ +name: CROSS +type: signature +principal-submitters: +- Marco Baldi +- Alessandro Barenghi +- Sebastian Bitzer +- Patrick Karl +- Felice Manganiello +- Alessio Pavoni +- Gerardo Pelosi +- Paolo Santini +- Jonas Schupp +- Freeman Slaughter +- Antonia Wachter-Zeh +- Violetta Weger +auxiliary-submitters: +- Marco Gianvecchio +crypto-assumption: hardness of the restricted syndrome decoding problem for random + linear codes on a finite field +website: https://www.cross-crypto.com/ +nist-round: 1 +spec-version: 1.2 + Keccak_x4 + PQClean fixes +primary-upstream: + source: https://github.com/rtjk/CROSS-PQClean/commit/577d7c761c684637923c8648644cf2f4d7b41954 + spdx-license-identifier: CC0-1.0 +parameter-sets: +- name: cross-rsdp-128-balanced + oqs_alg: OQS_SIG_alg_cross_rsdp_128_balanced + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 77 + length-secret-key: 32 + length-signature: 12912 + implementations-switch-on-runtime-cpu-features: false + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdp-128-fast + oqs_alg: OQS_SIG_alg_cross_rsdp_128_fast + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 77 + length-secret-key: 32 + length-signature: 19152 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdp-128-small + oqs_alg: OQS_SIG_alg_cross_rsdp_128_small + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 77 + length-secret-key: 32 + length-signature: 10080 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true +- name: cross-rsdp-192-balanced + oqs_alg: OQS_SIG_alg_cross_rsdp_192_balanced + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 115 + length-secret-key: 48 + length-signature: 28222 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdp-192-fast + oqs_alg: OQS_SIG_alg_cross_rsdp_192_fast + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 115 + length-secret-key: 48 + length-signature: 42682 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdp-192-small + oqs_alg: OQS_SIG_alg_cross_rsdp_192_small + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 115 + length-secret-key: 48 + length-signature: 23642 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true +- name: cross-rsdp-256-balanced + oqs_alg: OQS_SIG_alg_cross_rsdp_256_balanced + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 153 + length-secret-key: 64 + length-signature: 51056 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true +- name: cross-rsdp-256-fast + oqs_alg: OQS_SIG_alg_cross_rsdp_256_fast + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 153 + length-secret-key: 64 + length-signature: 76298 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdp-256-small + oqs_alg: OQS_SIG_alg_cross_rsdp_256_small + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 153 + length-secret-key: 64 + length-signature: 43592 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true +- name: cross-rsdpg-128-balanced + oqs_alg: OQS_SIG_alg_cross_rsdpg_128_balanced + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 54 + length-secret-key: 32 + length-signature: 9236 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-128-fast + oqs_alg: OQS_SIG_alg_cross_rsdpg_128_fast + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 54 + length-secret-key: 32 + length-signature: 12472 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-128-small + oqs_alg: OQS_SIG_alg_cross_rsdpg_128_small + claimed-nist-level: 1 + claimed-security: EUF-CMA + length-public-key: 54 + length-secret-key: 32 + length-signature: 7956 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-192-balanced + oqs_alg: OQS_SIG_alg_cross_rsdpg_192_balanced + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 83 + length-secret-key: 48 + length-signature: 23380 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-192-fast + oqs_alg: OQS_SIG_alg_cross_rsdpg_192_fast + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 83 + length-secret-key: 48 + length-signature: 27404 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-192-small + oqs_alg: OQS_SIG_alg_cross_rsdpg_192_small + claimed-nist-level: 3 + claimed-security: EUF-CMA + length-public-key: 83 + length-secret-key: 48 + length-signature: 18188 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true +- name: cross-rsdpg-256-balanced + oqs_alg: OQS_SIG_alg_cross_rsdpg_256_balanced + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 106 + length-secret-key: 64 + length-signature: 40134 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-256-fast + oqs_alg: OQS_SIG_alg_cross_rsdpg_256_fast + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 106 + length-secret-key: 64 + length-signature: 48938 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: false +- name: cross-rsdpg-256-small + oqs_alg: OQS_SIG_alg_cross_rsdpg_256_small + claimed-nist-level: 5 + claimed-security: EUF-CMA + length-public-key: 106 + length-secret-key: 64 + length-signature: 32742 + implementations-switch-on-runtime-cpu-features: true + implementations: + - upstream: primary-upstream + upstream-id: clean + supported-platforms: all + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true + - upstream: primary-upstream + upstream-id: avx2 + supported-platforms: + - architecture: x86_64 + required_flags: + - avx2 + common-crypto: + - SHA3: liboqs + no-secret-dependent-branching-claimed: true + no-secret-dependent-branching-checked-by-valgrind: true + large-stack-usage: true diff --git a/docs/cbom.json b/docs/cbom.json index 2fab7718a..d3bfad1d6 100644 --- a/docs/cbom.json +++ b/docs/cbom.json @@ -1199,6 +1199,726 @@ "nistQuantumSecurityLevel": 2 } }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-128-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-128-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-192-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-192-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdp-256-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdp-256-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-128-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-128-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 1 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-192-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-192-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 3 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-balanced:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-balanced:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-balanced", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-fast:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-fast:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-fast", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-small:generic", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "generic" + }, + "nistQuantumSecurityLevel": 5 + } + }, + { + "type": "crypto-asset", + "bom-ref": "alg:cross-rsdpg-256-small:x86_64", + "name": "CROSS", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "variant": "cross-rsdpg-256-small", + "primitive": "signature", + "implementationLevel": "softwarePlainRam", + "cryptoFunctions": [ + "keygen", + "sign", + "verify" + ], + "implementationPlatform": "x86_64" + }, + "nistQuantumSecurityLevel": 5 + } + }, { "type": "crypto-asset", "bom-ref": "alg:Dilithium2:generic", @@ -2469,6 +3189,42 @@ "alg:ML-KEM-1024:x86_64", "alg:sntrup761:generic", "alg:sntrup761:x86_64", + "alg:cross-rsdp-128-balanced:generic", + "alg:cross-rsdp-128-balanced:x86_64", + "alg:cross-rsdp-128-fast:generic", + "alg:cross-rsdp-128-fast:x86_64", + "alg:cross-rsdp-128-small:generic", + "alg:cross-rsdp-128-small:x86_64", + "alg:cross-rsdp-192-balanced:generic", + "alg:cross-rsdp-192-balanced:x86_64", + "alg:cross-rsdp-192-fast:generic", + "alg:cross-rsdp-192-fast:x86_64", + "alg:cross-rsdp-192-small:generic", + "alg:cross-rsdp-192-small:x86_64", + "alg:cross-rsdp-256-balanced:generic", + "alg:cross-rsdp-256-balanced:x86_64", + "alg:cross-rsdp-256-fast:generic", + "alg:cross-rsdp-256-fast:x86_64", + "alg:cross-rsdp-256-small:generic", + "alg:cross-rsdp-256-small:x86_64", + "alg:cross-rsdpg-128-balanced:generic", + "alg:cross-rsdpg-128-balanced:x86_64", + "alg:cross-rsdpg-128-fast:generic", + "alg:cross-rsdpg-128-fast:x86_64", + "alg:cross-rsdpg-128-small:generic", + "alg:cross-rsdpg-128-small:x86_64", + "alg:cross-rsdpg-192-balanced:generic", + "alg:cross-rsdpg-192-balanced:x86_64", + "alg:cross-rsdpg-192-fast:generic", + "alg:cross-rsdpg-192-fast:x86_64", + "alg:cross-rsdpg-192-small:generic", + "alg:cross-rsdpg-192-small:x86_64", + "alg:cross-rsdpg-256-balanced:generic", + "alg:cross-rsdpg-256-balanced:x86_64", + "alg:cross-rsdpg-256-fast:generic", + "alg:cross-rsdpg-256-fast:x86_64", + "alg:cross-rsdpg-256-small:generic", + "alg:cross-rsdpg-256-small:x86_64", "alg:Dilithium2:generic", "alg:Dilithium2:x86_64", "alg:Dilithium2:armv8-a", @@ -2948,6 +3704,258 @@ ], "dependencyType": "uses" }, + { + "ref": "alg:cross-rsdp-128-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-128-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-128-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-128-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-128-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-128-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-192-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdp-256-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-128-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-192-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-balanced:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-balanced:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-fast:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-fast:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-small:generic", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, + { + "ref": "alg:cross-rsdpg-256-small:x86_64", + "dependsOn": [ + "alg:sha3" + ], + "dependencyType": "uses" + }, { "ref": "alg:Dilithium2:generic", "dependsOn": [ diff --git a/scripts/copy_from_upstream/copy_from_upstream.yml b/scripts/copy_from_upstream/copy_from_upstream.yml index 216a99ae1..a8d70af43 100644 --- a/scripts/copy_from_upstream/copy_from_upstream.yml +++ b/scripts/copy_from_upstream/copy_from_upstream.yml @@ -61,6 +61,13 @@ upstreams: sig_meta_path: 'META/{pretty_name_full}_META.yml' sig_scheme_path: '.' patches: [pqmayo-aes.patch, pqmayo-mem.patch] + - + name: upcross + git_url: https://github.com/rtjk/CROSS-PQClean.git + git_branch: master + git_commit: 577d7c761c684637923c8648644cf2f4d7b41954 + sig_meta_path: 'generate/crypto_sign/{pqclean_scheme}/META.yml' + sig_scheme_path: 'generate/crypto_sign/{pqclean_scheme}' kems: - name: classic_mceliece @@ -341,3 +348,98 @@ sigs: pqclean_scheme: mayo-5 pretty_name_full: MAYO-5 signed_msg_order: sig_then_msg + - + name: cross + default_implementation: clean + upstream_location: upcross + schemes: + - + scheme: "rsdp_128_balanced" + pqclean_scheme: cross-rsdp-128-balanced + pretty_name_full: cross-rsdp-128-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdp_128_fast" + pqclean_scheme: cross-rsdp-128-fast + pretty_name_full: cross-rsdp-128-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdp_128_small" + pqclean_scheme: cross-rsdp-128-small + pretty_name_full: cross-rsdp-128-small + signed_msg_order: msg_then_sig + - + scheme: "rsdp_192_balanced" + pqclean_scheme: cross-rsdp-192-balanced + pretty_name_full: cross-rsdp-192-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdp_192_fast" + pqclean_scheme: cross-rsdp-192-fast + pretty_name_full: cross-rsdp-192-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdp_192_small" + pqclean_scheme: cross-rsdp-192-small + pretty_name_full: cross-rsdp-192-small + signed_msg_order: msg_then_sig + - + scheme: "rsdp_256_balanced" + pqclean_scheme: cross-rsdp-256-balanced + pretty_name_full: cross-rsdp-256-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdp_256_fast" + pqclean_scheme: cross-rsdp-256-fast + pretty_name_full: cross-rsdp-256-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdp_256_small" + pqclean_scheme: cross-rsdp-256-small + pretty_name_full: cross-rsdp-256-small + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_128_balanced" + pqclean_scheme: cross-rsdpg-128-balanced + pretty_name_full: cross-rsdpg-128-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_128_fast" + pqclean_scheme: cross-rsdpg-128-fast + pretty_name_full: cross-rsdpg-128-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_128_small" + pqclean_scheme: cross-rsdpg-128-small + pretty_name_full: cross-rsdpg-128-small + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_192_balanced" + pqclean_scheme: cross-rsdpg-192-balanced + pretty_name_full: cross-rsdpg-192-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_192_fast" + pqclean_scheme: cross-rsdpg-192-fast + pretty_name_full: cross-rsdpg-192-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_192_small" + pqclean_scheme: cross-rsdpg-192-small + pretty_name_full: cross-rsdpg-192-small + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_256_balanced" + pqclean_scheme: cross-rsdpg-256-balanced + pretty_name_full: cross-rsdpg-256-balanced + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_256_fast" + pqclean_scheme: cross-rsdpg-256-fast + pretty_name_full: cross-rsdpg-256-fast + signed_msg_order: msg_then_sig + - + scheme: "rsdpg_256_small" + pqclean_scheme: cross-rsdpg-256-small + pretty_name_full: cross-rsdpg-256-small + signed_msg_order: msg_then_sig \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25a9b7408..debc9c59a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,10 @@ if(OQS_ENABLE_SIG_MAYO) add_subdirectory(sig/mayo) set(SIG_OBJS ${SIG_OBJS} ${MAYO_OBJS}) endif() +if(OQS_ENABLE_SIG_CROSS) + add_subdirectory(sig/cross) + set(SIG_OBJS ${SIG_OBJS} ${CROSS_OBJS}) +endif() ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_ADD_ALG_OBJECTS_END if(OQS_ENABLE_SIG_STFL_XMSS) diff --git a/src/oqsconfig.h.cmake b/src/oqsconfig.h.cmake index 770202038..dae1babad 100644 --- a/src/oqsconfig.h.cmake +++ b/src/oqsconfig.h.cmake @@ -193,6 +193,44 @@ #cmakedefine OQS_ENABLE_SIG_mayo_3_avx2 1 #cmakedefine OQS_ENABLE_SIG_mayo_5 1 #cmakedefine OQS_ENABLE_SIG_mayo_5_avx2 1 + +#cmakedefine OQS_ENABLE_SIG_CROSS 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_128_small_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_192_small_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdp_256_small_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_balanced 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_fast 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_small 1 +#cmakedefine OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2 1 ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ADD_ALG_ENABLE_DEFINES_END ///// OQS_COPY_FROM_LIBJADE_FRAGMENT_ADD_ALG_ENABLE_DEFINES_START diff --git a/src/sig/cross/CMakeLists.txt b/src/sig/cross/CMakeLists.txt new file mode 100644 index 000000000..c05236381 --- /dev/null +++ b/src/sig/cross/CMakeLists.txt @@ -0,0 +1,278 @@ +# SPDX-License-Identifier: MIT + +# This file was generated by +# scripts/copy_from_upstream/copy_from_upstream.py + +set(_CROSS_OBJS "") + +if(OQS_ENABLE_SIG_cross_rsdp_128_balanced) + add_library(cross_rsdp_128_balanced_clean OBJECT sig_cross_rsdp_128_balanced.c upcross_cross-rsdp-128-balanced_clean/CROSS.c upcross_cross-rsdp-128-balanced_clean/csprng_hash.c upcross_cross-rsdp-128-balanced_clean/merkle.c upcross_cross-rsdp-128-balanced_clean/pack_unpack.c upcross_cross-rsdp-128-balanced_clean/seedtree.c upcross_cross-rsdp-128-balanced_clean/sign.c) + target_include_directories(cross_rsdp_128_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-balanced_clean) + target_include_directories(cross_rsdp_128_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2) + add_library(cross_rsdp_128_balanced_avx2 OBJECT upcross_cross-rsdp-128-balanced_avx2/CROSS.c upcross_cross-rsdp-128-balanced_avx2/csprng_hash.c upcross_cross-rsdp-128-balanced_avx2/merkle.c upcross_cross-rsdp-128-balanced_avx2/pack_unpack.c upcross_cross-rsdp-128-balanced_avx2/seedtree.c upcross_cross-rsdp-128-balanced_avx2/sign.c) + target_include_directories(cross_rsdp_128_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-balanced_avx2) + target_include_directories(cross_rsdp_128_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_128_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_128_fast) + add_library(cross_rsdp_128_fast_clean OBJECT sig_cross_rsdp_128_fast.c upcross_cross-rsdp-128-fast_clean/CROSS.c upcross_cross-rsdp-128-fast_clean/csprng_hash.c upcross_cross-rsdp-128-fast_clean/merkle.c upcross_cross-rsdp-128-fast_clean/pack_unpack.c upcross_cross-rsdp-128-fast_clean/seedtree.c upcross_cross-rsdp-128-fast_clean/sign.c) + target_include_directories(cross_rsdp_128_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-fast_clean) + target_include_directories(cross_rsdp_128_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2) + add_library(cross_rsdp_128_fast_avx2 OBJECT upcross_cross-rsdp-128-fast_avx2/CROSS.c upcross_cross-rsdp-128-fast_avx2/csprng_hash.c upcross_cross-rsdp-128-fast_avx2/merkle.c upcross_cross-rsdp-128-fast_avx2/pack_unpack.c upcross_cross-rsdp-128-fast_avx2/seedtree.c upcross_cross-rsdp-128-fast_avx2/sign.c) + target_include_directories(cross_rsdp_128_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-fast_avx2) + target_include_directories(cross_rsdp_128_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_128_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_128_small) + add_library(cross_rsdp_128_small_clean OBJECT sig_cross_rsdp_128_small.c upcross_cross-rsdp-128-small_clean/CROSS.c upcross_cross-rsdp-128-small_clean/csprng_hash.c upcross_cross-rsdp-128-small_clean/merkle.c upcross_cross-rsdp-128-small_clean/pack_unpack.c upcross_cross-rsdp-128-small_clean/seedtree.c upcross_cross-rsdp-128-small_clean/sign.c) + target_include_directories(cross_rsdp_128_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-small_clean) + target_include_directories(cross_rsdp_128_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2) + add_library(cross_rsdp_128_small_avx2 OBJECT upcross_cross-rsdp-128-small_avx2/CROSS.c upcross_cross-rsdp-128-small_avx2/csprng_hash.c upcross_cross-rsdp-128-small_avx2/merkle.c upcross_cross-rsdp-128-small_avx2/pack_unpack.c upcross_cross-rsdp-128-small_avx2/seedtree.c upcross_cross-rsdp-128-small_avx2/sign.c) + target_include_directories(cross_rsdp_128_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-128-small_avx2) + target_include_directories(cross_rsdp_128_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_128_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_balanced) + add_library(cross_rsdp_192_balanced_clean OBJECT sig_cross_rsdp_192_balanced.c upcross_cross-rsdp-192-balanced_clean/CROSS.c upcross_cross-rsdp-192-balanced_clean/csprng_hash.c upcross_cross-rsdp-192-balanced_clean/merkle.c upcross_cross-rsdp-192-balanced_clean/pack_unpack.c upcross_cross-rsdp-192-balanced_clean/seedtree.c upcross_cross-rsdp-192-balanced_clean/sign.c) + target_include_directories(cross_rsdp_192_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-balanced_clean) + target_include_directories(cross_rsdp_192_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2) + add_library(cross_rsdp_192_balanced_avx2 OBJECT upcross_cross-rsdp-192-balanced_avx2/CROSS.c upcross_cross-rsdp-192-balanced_avx2/csprng_hash.c upcross_cross-rsdp-192-balanced_avx2/merkle.c upcross_cross-rsdp-192-balanced_avx2/pack_unpack.c upcross_cross-rsdp-192-balanced_avx2/seedtree.c upcross_cross-rsdp-192-balanced_avx2/sign.c) + target_include_directories(cross_rsdp_192_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-balanced_avx2) + target_include_directories(cross_rsdp_192_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_192_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_fast) + add_library(cross_rsdp_192_fast_clean OBJECT sig_cross_rsdp_192_fast.c upcross_cross-rsdp-192-fast_clean/CROSS.c upcross_cross-rsdp-192-fast_clean/csprng_hash.c upcross_cross-rsdp-192-fast_clean/merkle.c upcross_cross-rsdp-192-fast_clean/pack_unpack.c upcross_cross-rsdp-192-fast_clean/seedtree.c upcross_cross-rsdp-192-fast_clean/sign.c) + target_include_directories(cross_rsdp_192_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-fast_clean) + target_include_directories(cross_rsdp_192_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2) + add_library(cross_rsdp_192_fast_avx2 OBJECT upcross_cross-rsdp-192-fast_avx2/CROSS.c upcross_cross-rsdp-192-fast_avx2/csprng_hash.c upcross_cross-rsdp-192-fast_avx2/merkle.c upcross_cross-rsdp-192-fast_avx2/pack_unpack.c upcross_cross-rsdp-192-fast_avx2/seedtree.c upcross_cross-rsdp-192-fast_avx2/sign.c) + target_include_directories(cross_rsdp_192_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-fast_avx2) + target_include_directories(cross_rsdp_192_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_192_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_small) + add_library(cross_rsdp_192_small_clean OBJECT sig_cross_rsdp_192_small.c upcross_cross-rsdp-192-small_clean/CROSS.c upcross_cross-rsdp-192-small_clean/csprng_hash.c upcross_cross-rsdp-192-small_clean/merkle.c upcross_cross-rsdp-192-small_clean/pack_unpack.c upcross_cross-rsdp-192-small_clean/seedtree.c upcross_cross-rsdp-192-small_clean/sign.c) + target_include_directories(cross_rsdp_192_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-small_clean) + target_include_directories(cross_rsdp_192_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2) + add_library(cross_rsdp_192_small_avx2 OBJECT upcross_cross-rsdp-192-small_avx2/CROSS.c upcross_cross-rsdp-192-small_avx2/csprng_hash.c upcross_cross-rsdp-192-small_avx2/merkle.c upcross_cross-rsdp-192-small_avx2/pack_unpack.c upcross_cross-rsdp-192-small_avx2/seedtree.c upcross_cross-rsdp-192-small_avx2/sign.c) + target_include_directories(cross_rsdp_192_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-192-small_avx2) + target_include_directories(cross_rsdp_192_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_192_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_balanced) + add_library(cross_rsdp_256_balanced_clean OBJECT sig_cross_rsdp_256_balanced.c upcross_cross-rsdp-256-balanced_clean/CROSS.c upcross_cross-rsdp-256-balanced_clean/csprng_hash.c upcross_cross-rsdp-256-balanced_clean/merkle.c upcross_cross-rsdp-256-balanced_clean/pack_unpack.c upcross_cross-rsdp-256-balanced_clean/seedtree.c upcross_cross-rsdp-256-balanced_clean/sign.c) + target_include_directories(cross_rsdp_256_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-balanced_clean) + target_include_directories(cross_rsdp_256_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2) + add_library(cross_rsdp_256_balanced_avx2 OBJECT upcross_cross-rsdp-256-balanced_avx2/CROSS.c upcross_cross-rsdp-256-balanced_avx2/csprng_hash.c upcross_cross-rsdp-256-balanced_avx2/merkle.c upcross_cross-rsdp-256-balanced_avx2/pack_unpack.c upcross_cross-rsdp-256-balanced_avx2/seedtree.c upcross_cross-rsdp-256-balanced_avx2/sign.c) + target_include_directories(cross_rsdp_256_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-balanced_avx2) + target_include_directories(cross_rsdp_256_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_256_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_fast) + add_library(cross_rsdp_256_fast_clean OBJECT sig_cross_rsdp_256_fast.c upcross_cross-rsdp-256-fast_clean/CROSS.c upcross_cross-rsdp-256-fast_clean/csprng_hash.c upcross_cross-rsdp-256-fast_clean/merkle.c upcross_cross-rsdp-256-fast_clean/pack_unpack.c upcross_cross-rsdp-256-fast_clean/seedtree.c upcross_cross-rsdp-256-fast_clean/sign.c) + target_include_directories(cross_rsdp_256_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-fast_clean) + target_include_directories(cross_rsdp_256_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2) + add_library(cross_rsdp_256_fast_avx2 OBJECT upcross_cross-rsdp-256-fast_avx2/CROSS.c upcross_cross-rsdp-256-fast_avx2/csprng_hash.c upcross_cross-rsdp-256-fast_avx2/merkle.c upcross_cross-rsdp-256-fast_avx2/pack_unpack.c upcross_cross-rsdp-256-fast_avx2/seedtree.c upcross_cross-rsdp-256-fast_avx2/sign.c) + target_include_directories(cross_rsdp_256_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-fast_avx2) + target_include_directories(cross_rsdp_256_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_256_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_small) + add_library(cross_rsdp_256_small_clean OBJECT sig_cross_rsdp_256_small.c upcross_cross-rsdp-256-small_clean/CROSS.c upcross_cross-rsdp-256-small_clean/csprng_hash.c upcross_cross-rsdp-256-small_clean/merkle.c upcross_cross-rsdp-256-small_clean/pack_unpack.c upcross_cross-rsdp-256-small_clean/seedtree.c upcross_cross-rsdp-256-small_clean/sign.c) + target_include_directories(cross_rsdp_256_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-small_clean) + target_include_directories(cross_rsdp_256_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2) + add_library(cross_rsdp_256_small_avx2 OBJECT upcross_cross-rsdp-256-small_avx2/CROSS.c upcross_cross-rsdp-256-small_avx2/csprng_hash.c upcross_cross-rsdp-256-small_avx2/merkle.c upcross_cross-rsdp-256-small_avx2/pack_unpack.c upcross_cross-rsdp-256-small_avx2/seedtree.c upcross_cross-rsdp-256-small_avx2/sign.c) + target_include_directories(cross_rsdp_256_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdp-256-small_avx2) + target_include_directories(cross_rsdp_256_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdp_256_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_balanced) + add_library(cross_rsdpg_128_balanced_clean OBJECT sig_cross_rsdpg_128_balanced.c upcross_cross-rsdpg-128-balanced_clean/CROSS.c upcross_cross-rsdpg-128-balanced_clean/csprng_hash.c upcross_cross-rsdpg-128-balanced_clean/merkle.c upcross_cross-rsdpg-128-balanced_clean/pack_unpack.c upcross_cross-rsdpg-128-balanced_clean/seedtree.c upcross_cross-rsdpg-128-balanced_clean/sign.c) + target_include_directories(cross_rsdpg_128_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-balanced_clean) + target_include_directories(cross_rsdpg_128_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2) + add_library(cross_rsdpg_128_balanced_avx2 OBJECT upcross_cross-rsdpg-128-balanced_avx2/CROSS.c upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.c upcross_cross-rsdpg-128-balanced_avx2/merkle.c upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.c upcross_cross-rsdpg-128-balanced_avx2/seedtree.c upcross_cross-rsdpg-128-balanced_avx2/sign.c) + target_include_directories(cross_rsdpg_128_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-balanced_avx2) + target_include_directories(cross_rsdpg_128_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_128_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_fast) + add_library(cross_rsdpg_128_fast_clean OBJECT sig_cross_rsdpg_128_fast.c upcross_cross-rsdpg-128-fast_clean/CROSS.c upcross_cross-rsdpg-128-fast_clean/csprng_hash.c upcross_cross-rsdpg-128-fast_clean/merkle.c upcross_cross-rsdpg-128-fast_clean/pack_unpack.c upcross_cross-rsdpg-128-fast_clean/seedtree.c upcross_cross-rsdpg-128-fast_clean/sign.c) + target_include_directories(cross_rsdpg_128_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-fast_clean) + target_include_directories(cross_rsdpg_128_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2) + add_library(cross_rsdpg_128_fast_avx2 OBJECT upcross_cross-rsdpg-128-fast_avx2/CROSS.c upcross_cross-rsdpg-128-fast_avx2/csprng_hash.c upcross_cross-rsdpg-128-fast_avx2/merkle.c upcross_cross-rsdpg-128-fast_avx2/pack_unpack.c upcross_cross-rsdpg-128-fast_avx2/seedtree.c upcross_cross-rsdpg-128-fast_avx2/sign.c) + target_include_directories(cross_rsdpg_128_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-fast_avx2) + target_include_directories(cross_rsdpg_128_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_128_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_small) + add_library(cross_rsdpg_128_small_clean OBJECT sig_cross_rsdpg_128_small.c upcross_cross-rsdpg-128-small_clean/CROSS.c upcross_cross-rsdpg-128-small_clean/csprng_hash.c upcross_cross-rsdpg-128-small_clean/merkle.c upcross_cross-rsdpg-128-small_clean/pack_unpack.c upcross_cross-rsdpg-128-small_clean/seedtree.c upcross_cross-rsdpg-128-small_clean/sign.c) + target_include_directories(cross_rsdpg_128_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-small_clean) + target_include_directories(cross_rsdpg_128_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2) + add_library(cross_rsdpg_128_small_avx2 OBJECT upcross_cross-rsdpg-128-small_avx2/CROSS.c upcross_cross-rsdpg-128-small_avx2/csprng_hash.c upcross_cross-rsdpg-128-small_avx2/merkle.c upcross_cross-rsdpg-128-small_avx2/pack_unpack.c upcross_cross-rsdpg-128-small_avx2/seedtree.c upcross_cross-rsdpg-128-small_avx2/sign.c) + target_include_directories(cross_rsdpg_128_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-128-small_avx2) + target_include_directories(cross_rsdpg_128_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_128_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_balanced) + add_library(cross_rsdpg_192_balanced_clean OBJECT sig_cross_rsdpg_192_balanced.c upcross_cross-rsdpg-192-balanced_clean/CROSS.c upcross_cross-rsdpg-192-balanced_clean/csprng_hash.c upcross_cross-rsdpg-192-balanced_clean/merkle.c upcross_cross-rsdpg-192-balanced_clean/pack_unpack.c upcross_cross-rsdpg-192-balanced_clean/seedtree.c upcross_cross-rsdpg-192-balanced_clean/sign.c) + target_include_directories(cross_rsdpg_192_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-balanced_clean) + target_include_directories(cross_rsdpg_192_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2) + add_library(cross_rsdpg_192_balanced_avx2 OBJECT upcross_cross-rsdpg-192-balanced_avx2/CROSS.c upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.c upcross_cross-rsdpg-192-balanced_avx2/merkle.c upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.c upcross_cross-rsdpg-192-balanced_avx2/seedtree.c upcross_cross-rsdpg-192-balanced_avx2/sign.c) + target_include_directories(cross_rsdpg_192_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-balanced_avx2) + target_include_directories(cross_rsdpg_192_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_192_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_fast) + add_library(cross_rsdpg_192_fast_clean OBJECT sig_cross_rsdpg_192_fast.c upcross_cross-rsdpg-192-fast_clean/CROSS.c upcross_cross-rsdpg-192-fast_clean/csprng_hash.c upcross_cross-rsdpg-192-fast_clean/merkle.c upcross_cross-rsdpg-192-fast_clean/pack_unpack.c upcross_cross-rsdpg-192-fast_clean/seedtree.c upcross_cross-rsdpg-192-fast_clean/sign.c) + target_include_directories(cross_rsdpg_192_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-fast_clean) + target_include_directories(cross_rsdpg_192_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2) + add_library(cross_rsdpg_192_fast_avx2 OBJECT upcross_cross-rsdpg-192-fast_avx2/CROSS.c upcross_cross-rsdpg-192-fast_avx2/csprng_hash.c upcross_cross-rsdpg-192-fast_avx2/merkle.c upcross_cross-rsdpg-192-fast_avx2/pack_unpack.c upcross_cross-rsdpg-192-fast_avx2/seedtree.c upcross_cross-rsdpg-192-fast_avx2/sign.c) + target_include_directories(cross_rsdpg_192_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-fast_avx2) + target_include_directories(cross_rsdpg_192_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_192_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_small) + add_library(cross_rsdpg_192_small_clean OBJECT sig_cross_rsdpg_192_small.c upcross_cross-rsdpg-192-small_clean/CROSS.c upcross_cross-rsdpg-192-small_clean/csprng_hash.c upcross_cross-rsdpg-192-small_clean/merkle.c upcross_cross-rsdpg-192-small_clean/pack_unpack.c upcross_cross-rsdpg-192-small_clean/seedtree.c upcross_cross-rsdpg-192-small_clean/sign.c) + target_include_directories(cross_rsdpg_192_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-small_clean) + target_include_directories(cross_rsdpg_192_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2) + add_library(cross_rsdpg_192_small_avx2 OBJECT upcross_cross-rsdpg-192-small_avx2/CROSS.c upcross_cross-rsdpg-192-small_avx2/csprng_hash.c upcross_cross-rsdpg-192-small_avx2/merkle.c upcross_cross-rsdpg-192-small_avx2/pack_unpack.c upcross_cross-rsdpg-192-small_avx2/seedtree.c upcross_cross-rsdpg-192-small_avx2/sign.c) + target_include_directories(cross_rsdpg_192_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-192-small_avx2) + target_include_directories(cross_rsdpg_192_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_192_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_balanced) + add_library(cross_rsdpg_256_balanced_clean OBJECT sig_cross_rsdpg_256_balanced.c upcross_cross-rsdpg-256-balanced_clean/CROSS.c upcross_cross-rsdpg-256-balanced_clean/csprng_hash.c upcross_cross-rsdpg-256-balanced_clean/merkle.c upcross_cross-rsdpg-256-balanced_clean/pack_unpack.c upcross_cross-rsdpg-256-balanced_clean/seedtree.c upcross_cross-rsdpg-256-balanced_clean/sign.c) + target_include_directories(cross_rsdpg_256_balanced_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-balanced_clean) + target_include_directories(cross_rsdpg_256_balanced_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2) + add_library(cross_rsdpg_256_balanced_avx2 OBJECT upcross_cross-rsdpg-256-balanced_avx2/CROSS.c upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.c upcross_cross-rsdpg-256-balanced_avx2/merkle.c upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.c upcross_cross-rsdpg-256-balanced_avx2/seedtree.c upcross_cross-rsdpg-256-balanced_avx2/sign.c) + target_include_directories(cross_rsdpg_256_balanced_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-balanced_avx2) + target_include_directories(cross_rsdpg_256_balanced_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_256_balanced_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_fast) + add_library(cross_rsdpg_256_fast_clean OBJECT sig_cross_rsdpg_256_fast.c upcross_cross-rsdpg-256-fast_clean/CROSS.c upcross_cross-rsdpg-256-fast_clean/csprng_hash.c upcross_cross-rsdpg-256-fast_clean/merkle.c upcross_cross-rsdpg-256-fast_clean/pack_unpack.c upcross_cross-rsdpg-256-fast_clean/seedtree.c upcross_cross-rsdpg-256-fast_clean/sign.c) + target_include_directories(cross_rsdpg_256_fast_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-fast_clean) + target_include_directories(cross_rsdpg_256_fast_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2) + add_library(cross_rsdpg_256_fast_avx2 OBJECT upcross_cross-rsdpg-256-fast_avx2/CROSS.c upcross_cross-rsdpg-256-fast_avx2/csprng_hash.c upcross_cross-rsdpg-256-fast_avx2/merkle.c upcross_cross-rsdpg-256-fast_avx2/pack_unpack.c upcross_cross-rsdpg-256-fast_avx2/seedtree.c upcross_cross-rsdpg-256-fast_avx2/sign.c) + target_include_directories(cross_rsdpg_256_fast_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-fast_avx2) + target_include_directories(cross_rsdpg_256_fast_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_256_fast_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_small) + add_library(cross_rsdpg_256_small_clean OBJECT sig_cross_rsdpg_256_small.c upcross_cross-rsdpg-256-small_clean/CROSS.c upcross_cross-rsdpg-256-small_clean/csprng_hash.c upcross_cross-rsdpg-256-small_clean/merkle.c upcross_cross-rsdpg-256-small_clean/pack_unpack.c upcross_cross-rsdpg-256-small_clean/seedtree.c upcross_cross-rsdpg-256-small_clean/sign.c) + target_include_directories(cross_rsdpg_256_small_clean PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-small_clean) + target_include_directories(cross_rsdpg_256_small_clean PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +if(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2) + add_library(cross_rsdpg_256_small_avx2 OBJECT upcross_cross-rsdpg-256-small_avx2/CROSS.c upcross_cross-rsdpg-256-small_avx2/csprng_hash.c upcross_cross-rsdpg-256-small_avx2/merkle.c upcross_cross-rsdpg-256-small_avx2/pack_unpack.c upcross_cross-rsdpg-256-small_avx2/seedtree.c upcross_cross-rsdpg-256-small_avx2/sign.c) + target_include_directories(cross_rsdpg_256_small_avx2 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/upcross_cross-rsdpg-256-small_avx2) + target_include_directories(cross_rsdpg_256_small_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/src/common/pqclean_shims) + target_compile_options(cross_rsdpg_256_small_avx2 PRIVATE -mavx2) + set(_CROSS_OBJS ${_CROSS_OBJS} $) +endif() + +set(CROSS_OBJS ${_CROSS_OBJS} PARENT_SCOPE) diff --git a/src/sig/cross/sig_cross.h b/src/sig/cross/sig_cross.h new file mode 100644 index 000000000..641002643 --- /dev/null +++ b/src/sig/cross/sig_cross.h @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: MIT + +#ifndef OQS_SIG_CROSS_H +#define OQS_SIG_CROSS_H + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced) +#define OQS_SIG_cross_rsdp_128_balanced_length_public_key 77 +#define OQS_SIG_cross_rsdp_128_balanced_length_secret_key 32 +#define OQS_SIG_cross_rsdp_128_balanced_length_signature 12912 + +OQS_SIG *OQS_SIG_cross_rsdp_128_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_balanced_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_cross_rsdp_128_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast) +#define OQS_SIG_cross_rsdp_128_fast_length_public_key 77 +#define OQS_SIG_cross_rsdp_128_fast_length_secret_key 32 +#define OQS_SIG_cross_rsdp_128_fast_length_signature 19152 + +OQS_SIG *OQS_SIG_cross_rsdp_128_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_fast_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_cross_rsdp_128_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small) +#define OQS_SIG_cross_rsdp_128_small_length_public_key 77 +#define OQS_SIG_cross_rsdp_128_small_length_secret_key 32 +#define OQS_SIG_cross_rsdp_128_small_length_signature 10080 + +OQS_SIG *OQS_SIG_cross_rsdp_128_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_small_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_cross_rsdp_128_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced) +#define OQS_SIG_cross_rsdp_192_balanced_length_public_key 115 +#define OQS_SIG_cross_rsdp_192_balanced_length_secret_key 48 +#define OQS_SIG_cross_rsdp_192_balanced_length_signature 28222 + +OQS_SIG *OQS_SIG_cross_rsdp_192_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_balanced_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_cross_rsdp_192_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast) +#define OQS_SIG_cross_rsdp_192_fast_length_public_key 115 +#define OQS_SIG_cross_rsdp_192_fast_length_secret_key 48 +#define OQS_SIG_cross_rsdp_192_fast_length_signature 42682 + +OQS_SIG *OQS_SIG_cross_rsdp_192_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_fast_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_cross_rsdp_192_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small) +#define OQS_SIG_cross_rsdp_192_small_length_public_key 115 +#define OQS_SIG_cross_rsdp_192_small_length_secret_key 48 +#define OQS_SIG_cross_rsdp_192_small_length_signature 23642 + +OQS_SIG *OQS_SIG_cross_rsdp_192_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_small_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_cross_rsdp_192_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced) +#define OQS_SIG_cross_rsdp_256_balanced_length_public_key 153 +#define OQS_SIG_cross_rsdp_256_balanced_length_secret_key 64 +#define OQS_SIG_cross_rsdp_256_balanced_length_signature 51056 + +OQS_SIG *OQS_SIG_cross_rsdp_256_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_balanced_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_cross_rsdp_256_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast) +#define OQS_SIG_cross_rsdp_256_fast_length_public_key 153 +#define OQS_SIG_cross_rsdp_256_fast_length_secret_key 64 +#define OQS_SIG_cross_rsdp_256_fast_length_signature 76298 + +OQS_SIG *OQS_SIG_cross_rsdp_256_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_fast_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_cross_rsdp_256_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small) +#define OQS_SIG_cross_rsdp_256_small_length_public_key 153 +#define OQS_SIG_cross_rsdp_256_small_length_secret_key 64 +#define OQS_SIG_cross_rsdp_256_small_length_signature 43592 + +OQS_SIG *OQS_SIG_cross_rsdp_256_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_small_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_cross_rsdp_256_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced) +#define OQS_SIG_cross_rsdpg_128_balanced_length_public_key 54 +#define OQS_SIG_cross_rsdpg_128_balanced_length_secret_key 32 +#define OQS_SIG_cross_rsdpg_128_balanced_length_signature 9236 + +OQS_SIG *OQS_SIG_cross_rsdpg_128_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_balanced_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_cross_rsdpg_128_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast) +#define OQS_SIG_cross_rsdpg_128_fast_length_public_key 54 +#define OQS_SIG_cross_rsdpg_128_fast_length_secret_key 32 +#define OQS_SIG_cross_rsdpg_128_fast_length_signature 12472 + +OQS_SIG *OQS_SIG_cross_rsdpg_128_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_fast_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_cross_rsdpg_128_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small) +#define OQS_SIG_cross_rsdpg_128_small_length_public_key 54 +#define OQS_SIG_cross_rsdpg_128_small_length_secret_key 32 +#define OQS_SIG_cross_rsdpg_128_small_length_signature 7956 + +OQS_SIG *OQS_SIG_cross_rsdpg_128_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_small_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_cross_rsdpg_128_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced) +#define OQS_SIG_cross_rsdpg_192_balanced_length_public_key 83 +#define OQS_SIG_cross_rsdpg_192_balanced_length_secret_key 48 +#define OQS_SIG_cross_rsdpg_192_balanced_length_signature 23380 + +OQS_SIG *OQS_SIG_cross_rsdpg_192_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_balanced_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_cross_rsdpg_192_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast) +#define OQS_SIG_cross_rsdpg_192_fast_length_public_key 83 +#define OQS_SIG_cross_rsdpg_192_fast_length_secret_key 48 +#define OQS_SIG_cross_rsdpg_192_fast_length_signature 27404 + +OQS_SIG *OQS_SIG_cross_rsdpg_192_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_fast_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_cross_rsdpg_192_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small) +#define OQS_SIG_cross_rsdpg_192_small_length_public_key 83 +#define OQS_SIG_cross_rsdpg_192_small_length_secret_key 48 +#define OQS_SIG_cross_rsdpg_192_small_length_signature 18188 + +OQS_SIG *OQS_SIG_cross_rsdpg_192_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_small_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_cross_rsdpg_192_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced) +#define OQS_SIG_cross_rsdpg_256_balanced_length_public_key 106 +#define OQS_SIG_cross_rsdpg_256_balanced_length_secret_key 64 +#define OQS_SIG_cross_rsdpg_256_balanced_length_signature 40134 + +OQS_SIG *OQS_SIG_cross_rsdpg_256_balanced_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_balanced_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_balanced_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_cross_rsdpg_256_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast) +#define OQS_SIG_cross_rsdpg_256_fast_length_public_key 106 +#define OQS_SIG_cross_rsdpg_256_fast_length_secret_key 64 +#define OQS_SIG_cross_rsdpg_256_fast_length_signature 48938 + +OQS_SIG *OQS_SIG_cross_rsdpg_256_fast_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_fast_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_fast_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_cross_rsdpg_256_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small) +#define OQS_SIG_cross_rsdpg_256_small_length_public_key 106 +#define OQS_SIG_cross_rsdpg_256_small_length_secret_key 64 +#define OQS_SIG_cross_rsdpg_256_small_length_signature 32742 + +OQS_SIG *OQS_SIG_cross_rsdpg_256_small_new(void); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_small_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_small_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_cross_rsdpg_256_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_128_balanced.c b/src/sig/cross/sig_cross_rsdp_128_balanced.c new file mode 100644 index 000000000..046000423 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_128_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced) + +OQS_SIG *OQS_SIG_cross_rsdp_128_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_128_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_128_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_128_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_128_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_128_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdp_128_balanced_sign; + sig->verify = OQS_SIG_cross_rsdp_128_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2) +extern int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_128_fast.c b/src/sig/cross/sig_cross_rsdp_128_fast.c new file mode 100644 index 000000000..5ad9f0ae3 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_128_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast) + +OQS_SIG *OQS_SIG_cross_rsdp_128_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_128_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_128_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_128_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_128_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_128_fast_keypair; + sig->sign = OQS_SIG_cross_rsdp_128_fast_sign; + sig->verify = OQS_SIG_cross_rsdp_128_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2) +extern int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_128_small.c b/src/sig/cross/sig_cross_rsdp_128_small.c new file mode 100644 index 000000000..24fb6f6cb --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_128_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small) + +OQS_SIG *OQS_SIG_cross_rsdp_128_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_128_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_128_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_128_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_128_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_128_small_keypair; + sig->sign = OQS_SIG_cross_rsdp_128_small_sign; + sig->verify = OQS_SIG_cross_rsdp_128_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2) +extern int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_128_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_192_balanced.c b/src/sig/cross/sig_cross_rsdp_192_balanced.c new file mode 100644 index 000000000..f4cc950cb --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_192_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced) + +OQS_SIG *OQS_SIG_cross_rsdp_192_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_192_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_192_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_192_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_192_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_192_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdp_192_balanced_sign; + sig->verify = OQS_SIG_cross_rsdp_192_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2) +extern int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_192_fast.c b/src/sig/cross/sig_cross_rsdp_192_fast.c new file mode 100644 index 000000000..8bed778f6 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_192_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast) + +OQS_SIG *OQS_SIG_cross_rsdp_192_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_192_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_192_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_192_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_192_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_192_fast_keypair; + sig->sign = OQS_SIG_cross_rsdp_192_fast_sign; + sig->verify = OQS_SIG_cross_rsdp_192_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2) +extern int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_192_small.c b/src/sig/cross/sig_cross_rsdp_192_small.c new file mode 100644 index 000000000..53864a866 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_192_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small) + +OQS_SIG *OQS_SIG_cross_rsdp_192_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_192_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_192_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_192_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_192_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_192_small_keypair; + sig->sign = OQS_SIG_cross_rsdp_192_small_sign; + sig->verify = OQS_SIG_cross_rsdp_192_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2) +extern int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_192_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_256_balanced.c b/src/sig/cross/sig_cross_rsdp_256_balanced.c new file mode 100644 index 000000000..3cd07a60b --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_256_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced) + +OQS_SIG *OQS_SIG_cross_rsdp_256_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_256_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_256_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_256_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_256_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_256_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdp_256_balanced_sign; + sig->verify = OQS_SIG_cross_rsdp_256_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2) +extern int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_256_fast.c b/src/sig/cross/sig_cross_rsdp_256_fast.c new file mode 100644 index 000000000..3c954f9a2 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_256_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast) + +OQS_SIG *OQS_SIG_cross_rsdp_256_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_256_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_256_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_256_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_256_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_256_fast_keypair; + sig->sign = OQS_SIG_cross_rsdp_256_fast_sign; + sig->verify = OQS_SIG_cross_rsdp_256_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2) +extern int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdp_256_small.c b/src/sig/cross/sig_cross_rsdp_256_small.c new file mode 100644 index 000000000..f20039a91 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdp_256_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small) + +OQS_SIG *OQS_SIG_cross_rsdp_256_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdp_256_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdp_256_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdp_256_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdp_256_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdp_256_small_keypair; + sig->sign = OQS_SIG_cross_rsdp_256_small_sign; + sig->verify = OQS_SIG_cross_rsdp_256_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2) +extern int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdp_256_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdp_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_128_balanced.c b/src/sig/cross/sig_cross_rsdpg_128_balanced.c new file mode 100644 index 000000000..287784771 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_128_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced) + +OQS_SIG *OQS_SIG_cross_rsdpg_128_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_128_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_128_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_128_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_128_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_128_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdpg_128_balanced_sign; + sig->verify = OQS_SIG_cross_rsdpg_128_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2) +extern int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_128_fast.c b/src/sig/cross/sig_cross_rsdpg_128_fast.c new file mode 100644 index 000000000..cf10d6298 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_128_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast) + +OQS_SIG *OQS_SIG_cross_rsdpg_128_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_128_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_128_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_128_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_128_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_128_fast_keypair; + sig->sign = OQS_SIG_cross_rsdpg_128_fast_sign; + sig->verify = OQS_SIG_cross_rsdpg_128_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2) +extern int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_128_small.c b/src/sig/cross/sig_cross_rsdpg_128_small.c new file mode 100644 index 000000000..e2cab5d43 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_128_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small) + +OQS_SIG *OQS_SIG_cross_rsdpg_128_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_128_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_128_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_128_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_128_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_128_small_keypair; + sig->sign = OQS_SIG_cross_rsdpg_128_small_sign; + sig->verify = OQS_SIG_cross_rsdpg_128_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2) +extern int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_128_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_128_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_192_balanced.c b/src/sig/cross/sig_cross_rsdpg_192_balanced.c new file mode 100644 index 000000000..e85be184b --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_192_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced) + +OQS_SIG *OQS_SIG_cross_rsdpg_192_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_192_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_192_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_192_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_192_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_192_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdpg_192_balanced_sign; + sig->verify = OQS_SIG_cross_rsdpg_192_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2) +extern int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_192_fast.c b/src/sig/cross/sig_cross_rsdpg_192_fast.c new file mode 100644 index 000000000..4c600ae14 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_192_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast) + +OQS_SIG *OQS_SIG_cross_rsdpg_192_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_192_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_192_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_192_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_192_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_192_fast_keypair; + sig->sign = OQS_SIG_cross_rsdpg_192_fast_sign; + sig->verify = OQS_SIG_cross_rsdpg_192_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2) +extern int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_192_small.c b/src/sig/cross/sig_cross_rsdpg_192_small.c new file mode 100644 index 000000000..624f02e45 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_192_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small) + +OQS_SIG *OQS_SIG_cross_rsdpg_192_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_192_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_192_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_192_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_192_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_192_small_keypair; + sig->sign = OQS_SIG_cross_rsdpg_192_small_sign; + sig->verify = OQS_SIG_cross_rsdpg_192_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2) +extern int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_192_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_192_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_256_balanced.c b/src/sig/cross/sig_cross_rsdpg_256_balanced.c new file mode 100644 index 000000000..73f12fafb --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_256_balanced.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced) + +OQS_SIG *OQS_SIG_cross_rsdpg_256_balanced_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_256_balanced; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_256_balanced_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_256_balanced_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_256_balanced_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_256_balanced_keypair; + sig->sign = OQS_SIG_cross_rsdpg_256_balanced_sign; + sig->verify = OQS_SIG_cross_rsdpg_256_balanced_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2) +extern int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_balanced_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_balanced_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_balanced_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_balanced_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_256_fast.c b/src/sig/cross/sig_cross_rsdpg_256_fast.c new file mode 100644 index 000000000..1ebfa2c22 --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_256_fast.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast) + +OQS_SIG *OQS_SIG_cross_rsdpg_256_fast_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_256_fast; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_256_fast_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_256_fast_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_256_fast_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_256_fast_keypair; + sig->sign = OQS_SIG_cross_rsdpg_256_fast_sign; + sig->verify = OQS_SIG_cross_rsdpg_256_fast_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2) +extern int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_fast_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_fast_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_fast_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_fast_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/sig_cross_rsdpg_256_small.c b/src/sig/cross/sig_cross_rsdpg_256_small.c new file mode 100644 index 000000000..6f105167e --- /dev/null +++ b/src/sig/cross/sig_cross_rsdpg_256_small.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +#include + +#include + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small) + +OQS_SIG *OQS_SIG_cross_rsdpg_256_small_new(void) { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_cross_rsdpg_256_small; + sig->alg_version = "1.2 + Keccak_x4 + PQClean fixes"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_cross_rsdpg_256_small_length_public_key; + sig->length_secret_key = OQS_SIG_cross_rsdpg_256_small_length_secret_key; + sig->length_signature = OQS_SIG_cross_rsdpg_256_small_length_signature; + + sig->keypair = OQS_SIG_cross_rsdpg_256_small_keypair; + sig->sign = OQS_SIG_cross_rsdpg_256_small_sign; + sig->verify = OQS_SIG_cross_rsdpg_256_small_verify; + + return sig; +} + +extern int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2) +extern int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +extern int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); +#endif + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_small_keypair(uint8_t *public_key, uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_keypair(public_key, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair(public_key, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_small_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +#endif +} + +OQS_API OQS_STATUS OQS_SIG_cross_rsdpg_256_small_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { +#if defined(OQS_ENABLE_SIG_cross_rsdpg_256_small_avx2) +#if defined(OQS_DIST_BUILD) + if (OQS_CPU_has_extension(OQS_CPU_EXT_AVX2)) { +#endif /* OQS_DIST_BUILD */ + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#if defined(OQS_DIST_BUILD) + } else { + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); + } +#endif /* OQS_DIST_BUILD */ +#else + return (OQS_STATUS) PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +#endif +} + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.c new file mode 100644 index 000000000..ec0add723 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.h new file mode 100644 index 000000000..8b716b31a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/api.h new file mode 100644 index 000000000..e93861eb6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdp-128-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_CRYPTO_BYTES 12912 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128BALANCED_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..02011ba99 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..7892cfde2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.c new file mode 100644 index 000000000..70d2bef68 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.h new file mode 100644 index 000000000..d550153e2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..dff03e070 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP128BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..7479c467c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..0c1b4f24d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/parameters.h new file mode 100644 index 000000000..6c9dc09f6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (252) +#define W (212) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 107 +#define NUM_NODES_SEED_TREE 504 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 63, 126, 252} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 1, 3} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 1817 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 2102 diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.c new file mode 100644 index 000000000..10617a6a0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP128BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.h new file mode 100644 index 000000000..79f6c3bab --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/set.h new file mode 100644 index 000000000..9257a8df8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sign.c new file mode 100644 index 000000000..810001ab1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.c new file mode 100644 index 000000000..0f84a39b5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.h new file mode 100644 index 000000000..60742a9df --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/api.h new file mode 100644 index 000000000..80f77b6fe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdp-128-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CRYPTO_BYTES 12912 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..56da4e442 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..c9c14549f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.c new file mode 100644 index 000000000..48b0e1dc8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.h new file mode 100644 index 000000000..27b55a90f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..b0d1fca2e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..a038e9bfd --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..24c46f00a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/parameters.h new file mode 100644 index 000000000..6c9dc09f6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (252) +#define W (212) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 107 +#define NUM_NODES_SEED_TREE 504 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 63, 126, 252} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 1, 3} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 1817 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 2102 diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.c new file mode 100644 index 000000000..0505c9e97 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.h new file mode 100644 index 000000000..23176bb01 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP128BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/set.h new file mode 100644 index 000000000..4db25c391 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sign.c new file mode 100644 index 000000000..4fefee044 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.c new file mode 100644 index 000000000..3924a8ecd --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.c @@ -0,0 +1,548 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP128FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP128FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.h new file mode 100644 index 000000000..47d73b422 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/api.h new file mode 100644 index 000000000..3e0e807a7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDP128FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128FAST_AVX2_CRYPTO_ALGNAME "cross-rsdp-128-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128FAST_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128FAST_AVX2_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128FAST_AVX2_CRYPTO_BYTES 19152 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128FAST_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..382354ea9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..0aae75c2f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.c new file mode 100644 index 000000000..1aa7c77a8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP128FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.h new file mode 100644 index 000000000..3c4d87135 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..2bc0a06df --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP128FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..f61e4a12a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..daaf46af8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/parameters.h new file mode 100644 index 000000000..42c4ccbb1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (163) +#define W ( 85) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 83 +#define NUM_NODES_SEED_TREE 330 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 11, 21, 41, 82, 163} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 8, 19, 42, 88} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 1187 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 1355 diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.c new file mode 100644 index 000000000..988fb17b2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDP128FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.h new file mode 100644 index 000000000..3301b739f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP128FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/set.h new file mode 100644 index 000000000..0881f5c42 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sign.c new file mode 100644 index 000000000..a66847244 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.c new file mode 100644 index 000000000..3ea218334 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.c @@ -0,0 +1,449 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP128FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP128FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.h new file mode 100644 index 000000000..e6d7f80c5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/api.h new file mode 100644 index 000000000..40252ff01 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdp-128-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_CRYPTO_BYTES 19152 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128FAST_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.c new file mode 100644 index 000000000..342ee105c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.h new file mode 100644 index 000000000..c0b9a5420 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.c new file mode 100644 index 000000000..eeacd0542 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.h new file mode 100644 index 000000000..70ea58c73 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle_tree.h new file mode 100644 index 000000000..feb0401de --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP128FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.c new file mode 100644 index 000000000..8e4f748c2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.h new file mode 100644 index 000000000..90d649e75 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/parameters.h new file mode 100644 index 000000000..42c4ccbb1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (163) +#define W ( 85) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 83 +#define NUM_NODES_SEED_TREE 330 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 11, 21, 41, 82, 163} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 8, 19, 42, 88} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 1187 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 1355 diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.c new file mode 100644 index 000000000..6d3ff088e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.h new file mode 100644 index 000000000..3c0b0a10f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/set.h new file mode 100644 index 000000000..4f73890c2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sign.c new file mode 100644 index 000000000..458923471 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.c new file mode 100644 index 000000000..b5b96a4b2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.h new file mode 100644 index 000000000..05aef13d2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/api.h new file mode 100644 index 000000000..c9a1cee1f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdp-128-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_CRYPTO_BYTES 10080 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128SMALL_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.c new file mode 100644 index 000000000..6fe036b0c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.h new file mode 100644 index 000000000..ddddf21d4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.c new file mode 100644 index 000000000..e77678149 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.h new file mode 100644 index 000000000..2331ce9e8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle_tree.h new file mode 100644 index 000000000..7edf564f7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP128SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.c new file mode 100644 index 000000000..ef5a225e5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.h new file mode 100644 index 000000000..d0bb106d9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/parameters.h new file mode 100644 index 000000000..dd59354b9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (960) +#define W (938) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 120 +#define NUM_NODES_SEED_TREE 1920 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 120, 240, 480, 960} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 15, 31, 63} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 6811 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 9371 diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.c new file mode 100644 index 000000000..79710f94a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP128SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.h new file mode 100644 index 000000000..4816b1eec --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/set.h new file mode 100644 index 000000000..8ac5d952e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sign.c new file mode 100644 index 000000000..506b20734 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.c new file mode 100644 index 000000000..4d9fddbb3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.h new file mode 100644 index 000000000..811998e43 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-128-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/api.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/api.h new file mode 100644 index 000000000..58a21b3f8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP128SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdp-128-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 77 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_CRYPTO_BYTES 10080 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP128SMALL_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.c new file mode 100644 index 000000000..8b6e0ce13 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP128SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.h new file mode 100644 index 000000000..5acaa2452 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.c new file mode 100644 index 000000000..1f9a7fbf9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.h new file mode 100644 index 000000000..1f031eaf1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle_tree.h new file mode 100644 index 000000000..fef8fc7a1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP128SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.c new file mode 100644 index 000000000..c3d1ef541 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.h new file mode 100644 index 000000000..b408ec571 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/parameters.h new file mode 100644 index 000000000..dd59354b9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N (127) +#define K ( 76) + +#define T (960) +#define W (938) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 120 +#define NUM_NODES_SEED_TREE 1920 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 120, 240, 480, 960} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 15, 31, 63} +#define BITS_N_ZQ_CT_RNG 923 +#define BITS_BETA_ZQSTAR_CT_RNG 6811 +#define BITS_V_CT_RNG 27260 +#define BITS_N_ZZ_CT_RNG 493 +#define BITS_CWSTR_RNG 9371 diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.c new file mode 100644 index 000000000..687799392 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP128SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.h new file mode 100644 index 000000000..f5eb8ba2b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP128SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/set.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/set.h new file mode 100644 index 000000000..e8aa5c98c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_1 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-128-small_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-128-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-128-small_clean/sign.c new file mode 100644 index 000000000..2fc4a1b6a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-128-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP128SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP128SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.c new file mode 100644 index 000000000..546e611c3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.h new file mode 100644 index 000000000..9ce2e25b1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/api.h new file mode 100644 index 000000000..3d24f11b5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdp-192-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_CRYPTO_BYTES 28222 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192BALANCED_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..8a437ddeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..2bc154c1d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.c new file mode 100644 index 000000000..121a16456 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.h new file mode 100644 index 000000000..1b13e7628 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..a485d5f7f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP192BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..1dc2b6c4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..3c6c2c0c6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/parameters.h new file mode 100644 index 000000000..f2c6dde00 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (398) +#define W (340) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 162 +#define NUM_NODES_SEED_TREE 799 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 7, 13, 25, 50, 100, 199, 398} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 1, 4, 11, 25, 53, 110} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 2868 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 3647 diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.c new file mode 100644 index 000000000..aeae44a39 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP192BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.h new file mode 100644 index 000000000..12d927a4c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/set.h new file mode 100644 index 000000000..0220a36b0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sign.c new file mode 100644 index 000000000..6e94f6cc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.c new file mode 100644 index 000000000..dac997824 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.h new file mode 100644 index 000000000..038612bf0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/api.h new file mode 100644 index 000000000..8772b35d8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdp-192-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CRYPTO_BYTES 28222 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..f756088b6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..c0c30a66e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.c new file mode 100644 index 000000000..81d7c12f9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.h new file mode 100644 index 000000000..217be8b4e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..c44b57397 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..3ef2f2bc8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..04d6ec032 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/parameters.h new file mode 100644 index 000000000..f2c6dde00 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (398) +#define W (340) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 162 +#define NUM_NODES_SEED_TREE 799 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 7, 13, 25, 50, 100, 199, 398} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 1, 4, 11, 25, 53, 110} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 2868 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 3647 diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.c new file mode 100644 index 000000000..3cd44db0d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.h new file mode 100644 index 000000000..c29d3ca72 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP192BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/set.h new file mode 100644 index 000000000..a84ae7a30 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sign.c new file mode 100644 index 000000000..a2d5f838f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.c new file mode 100644 index 000000000..ac027de43 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.c @@ -0,0 +1,548 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP192FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP192FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.h new file mode 100644 index 000000000..0c754a6ac --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/api.h new file mode 100644 index 000000000..61d14364f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDP192FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192FAST_AVX2_CRYPTO_ALGNAME "cross-rsdp-192-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192FAST_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192FAST_AVX2_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192FAST_AVX2_CRYPTO_BYTES 42682 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192FAST_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..7e63c32ae --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..c3bc2489c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.c new file mode 100644 index 000000000..c32ae58dd --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP192FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.h new file mode 100644 index 000000000..804b6f3f9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..43a20a33c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP192FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..8f71d24e6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..b105b2c95 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/parameters.h new file mode 100644 index 000000000..284b1ef9e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (245) +#define W (127) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 125 +#define NUM_NODES_SEED_TREE 492 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 62, 123, 245} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 3, 8} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 1785 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 2125 diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.c new file mode 100644 index 000000000..2a0b0d5c9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDP192FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.h new file mode 100644 index 000000000..bd5b29f17 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP192FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/set.h new file mode 100644 index 000000000..182445159 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sign.c new file mode 100644 index 000000000..25da26e59 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.c new file mode 100644 index 000000000..21c83d74b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.c @@ -0,0 +1,449 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP192FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP192FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.h new file mode 100644 index 000000000..9c9fea82d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/api.h new file mode 100644 index 000000000..6be7d2810 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdp-192-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_CRYPTO_BYTES 42682 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192FAST_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.c new file mode 100644 index 000000000..ef5be3b28 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.h new file mode 100644 index 000000000..1849cf2b3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.c new file mode 100644 index 000000000..086e59071 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.h new file mode 100644 index 000000000..a5c891c0e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle_tree.h new file mode 100644 index 000000000..2ab147f50 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP192FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.c new file mode 100644 index 000000000..91fc9e891 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.h new file mode 100644 index 000000000..2d7d5db3e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/parameters.h new file mode 100644 index 000000000..284b1ef9e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (245) +#define W (127) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 125 +#define NUM_NODES_SEED_TREE 492 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 62, 123, 245} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 3, 8} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 1785 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 2125 diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.c new file mode 100644 index 000000000..0a9f9d5ab --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.h new file mode 100644 index 000000000..4a5d0f904 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/set.h new file mode 100644 index 000000000..8fbf07a90 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sign.c new file mode 100644 index 000000000..86ff8d026 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.c new file mode 100644 index 000000000..afa5bf698 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.h new file mode 100644 index 000000000..975cb7138 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/api.h new file mode 100644 index 000000000..a9d72da46 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdp-192-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_CRYPTO_BYTES 23642 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192SMALL_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.c new file mode 100644 index 000000000..023f3e8ff --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.h new file mode 100644 index 000000000..5071313b9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.c new file mode 100644 index 000000000..21c70e2b2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.h new file mode 100644 index 000000000..be1e3de4c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle_tree.h new file mode 100644 index 000000000..b3a8a18d7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP192SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.c new file mode 100644 index 000000000..4993b6b82 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.h new file mode 100644 index 000000000..074c7ec1c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/parameters.h new file mode 100644 index 000000000..ba24da391 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (945) +#define W (907) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 177 +#define NUM_NODES_SEED_TREE 1894 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 119, 237, 473, 945} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 16, 35, 74} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 6730 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 9332 diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.c new file mode 100644 index 000000000..66603da1b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP192SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.h new file mode 100644 index 000000000..448fcd90d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/set.h new file mode 100644 index 000000000..1c1048a0d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sign.c new file mode 100644 index 000000000..7a8337d8f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.c new file mode 100644 index 000000000..2919b9f60 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.h new file mode 100644 index 000000000..291db9871 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-192-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/api.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/api.h new file mode 100644 index 000000000..e1ae23246 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP192SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdp-192-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 115 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_CRYPTO_BYTES 23642 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP192SMALL_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.c new file mode 100644 index 000000000..c6a697863 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP192SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.h new file mode 100644 index 000000000..3685f99b8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.c new file mode 100644 index 000000000..34969171b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.h new file mode 100644 index 000000000..48c089a60 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle_tree.h new file mode 100644 index 000000000..b5056c377 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP192SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.c new file mode 100644 index 000000000..0b89738da --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.h new file mode 100644 index 000000000..abfad3959 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/parameters.h new file mode 100644 index 000000000..ba24da391 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/parameters.h @@ -0,0 +1,131 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N (187) +#define K (111) + +#define T (945) +#define W (907) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 177 +#define NUM_NODES_SEED_TREE 1894 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 119, 237, 473, 945} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 16, 35, 74} +#define BITS_N_ZQ_CT_RNG 1361 +#define BITS_BETA_ZQSTAR_CT_RNG 6730 +#define BITS_V_CT_RNG 59289 +#define BITS_N_ZZ_CT_RNG 729 +#define BITS_CWSTR_RNG 9332 diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.c new file mode 100644 index 000000000..83ff7103a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP192SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.h new file mode 100644 index 000000000..4b27ec90a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP192SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/set.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/set.h new file mode 100644 index 000000000..091b5cdf4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_3 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-192-small_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-192-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-192-small_clean/sign.c new file mode 100644 index 000000000..6601194f9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-192-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP192SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP192SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.c new file mode 100644 index 000000000..5e07271bb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.h new file mode 100644 index 000000000..af30d5e85 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/api.h new file mode 100644 index 000000000..e492205ed --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdp-256-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_CRYPTO_BYTES 51056 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256BALANCED_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..79e49e031 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..57eb0aadf --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.c new file mode 100644 index 000000000..eaf92360e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.h new file mode 100644 index 000000000..aa7eb9ae7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..31b6110df --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP256BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..6d30a9db3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..c93edfe70 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/parameters.h new file mode 100644 index 000000000..73fc145cf --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (507) +#define W (427) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 214 +#define NUM_NODES_SEED_TREE 1015 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 64, 127, 254, 507} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 0, 1, 3} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 3658 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 4734 diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.c new file mode 100644 index 000000000..a3eaa0e96 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP256BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.h new file mode 100644 index 000000000..9551968ac --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/set.h new file mode 100644 index 000000000..74f90e654 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sign.c new file mode 100644 index 000000000..e60f11cae --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.c new file mode 100644 index 000000000..dca0f0402 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.h new file mode 100644 index 000000000..78cb959e8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/api.h new file mode 100644 index 000000000..e82e471f8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdp-256-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CRYPTO_BYTES 51056 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..48685a4ba --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..10e8b10c8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.c new file mode 100644 index 000000000..3a1ad979c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.h new file mode 100644 index 000000000..d029c73be --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..3c86c2449 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..7c315ece4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..76ce9950d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/parameters.h new file mode 100644 index 000000000..73fc145cf --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (507) +#define W (427) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 214 +#define NUM_NODES_SEED_TREE 1015 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 64, 127, 254, 507} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 0, 1, 3} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 3658 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 4734 diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.c new file mode 100644 index 000000000..6d3a0b7b3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.h new file mode 100644 index 000000000..aa7cb48be --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP256BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/set.h new file mode 100644 index 000000000..b89975154 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sign.c new file mode 100644 index 000000000..c92751517 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.c new file mode 100644 index 000000000..86402bf9b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.c @@ -0,0 +1,548 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP256FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP256FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.h new file mode 100644 index 000000000..5d7534c44 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/api.h new file mode 100644 index 000000000..a82c414d1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDP256FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256FAST_AVX2_CRYPTO_ALGNAME "cross-rsdp-256-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256FAST_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256FAST_AVX2_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256FAST_AVX2_CRYPTO_BYTES 76298 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256FAST_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..d75cce902 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..89f17f2f1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.c new file mode 100644 index 000000000..1d23b7e25 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP256FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.h new file mode 100644 index 000000000..750722f5a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..5358925c4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP256FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..34218585d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..b09f270b8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/parameters.h new file mode 100644 index 000000000..d683bf25e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (327) +#define W (169) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 166 +#define NUM_NODES_SEED_TREE 658 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 11, 21, 41, 82, 164, 327} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 8, 19, 42, 88, 180} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 2383 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 3044 diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.c new file mode 100644 index 000000000..c811b4b2a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDP256FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.h new file mode 100644 index 000000000..6ff4c4260 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP256FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/set.h new file mode 100644 index 000000000..55d1beb3f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sign.c new file mode 100644 index 000000000..383293b6c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.c new file mode 100644 index 000000000..eebafdc3f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.c @@ -0,0 +1,449 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP256FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDP256FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.h new file mode 100644 index 000000000..542948e9e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/api.h new file mode 100644 index 000000000..3fc8e33e6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdp-256-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_CRYPTO_BYTES 76298 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256FAST_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.c new file mode 100644 index 000000000..2d96c4b1f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.h new file mode 100644 index 000000000..293874625 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.c new file mode 100644 index 000000000..6b905d147 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.h new file mode 100644 index 000000000..436896997 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle_tree.h new file mode 100644 index 000000000..01bd12687 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDP256FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.c new file mode 100644 index 000000000..628da9474 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.h new file mode 100644 index 000000000..80d3869ba --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/parameters.h new file mode 100644 index 000000000..d683bf25e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (327) +#define W (169) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 166 +#define NUM_NODES_SEED_TREE 658 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 11, 21, 41, 82, 164, 327} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 8, 19, 42, 88, 180} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 2383 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 3044 diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.c new file mode 100644 index 000000000..e1d34ffe9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.h new file mode 100644 index 000000000..0cecfbd91 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/set.h new file mode 100644 index 000000000..b1f37d58d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sign.c new file mode 100644 index 000000000..a1ccc917c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.c new file mode 100644 index 000000000..d57332ae5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.c @@ -0,0 +1,553 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr_avx); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + /* Expanded */ + alignas(EPI8_PER_REG) uint16_t V_tr_avx[K][ROUND_UP(N - K, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + V_tr_avx[i][j] = V_tr[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr_avx); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.h new file mode 100644 index 000000000..3c5a07d48 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/api.h new file mode 100644 index 000000000..10112be21 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdp-256-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_CRYPTO_BYTES 43592 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256SMALL_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.c new file mode 100644 index 000000000..721307069 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.h new file mode 100644 index 000000000..c3eaa95c0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/fq_arith.h new file mode 100644 index 000000000..f30ac9702 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/fq_arith.h @@ -0,0 +1,225 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* Computes e * [I_k V]^T, V is already in transposed form + * since */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_DOUBLEPREC V_tr[K][ROUND_UP(N - K, EPI16_PER_REG)]) { + + alignas(EPI8_PER_REG) FQ_DOUBLEPREC res_dprec[ROUND_UP(N - K, EPI16_PER_REG)] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + + for (int i = 0; i < K; i++) { + for (int j = 0; j < ROUND_UP(N - K, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i V_tr_slice = _mm256_lddqu_si256( + (__m256i const *) &V_tr[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, V_tr_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_DOUBLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + + alignas(EPI8_PER_REG) FZ_ELEM e_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM utilde_align[ROUND_UP(N, EPI8_PER_REG)]; + alignas(EPI8_PER_REG) FQ_ELEM res_align[ROUND_UP(N, EPI8_PER_REG)]; + __m256i mred_mask = _mm256_set1_epi16 ((uint16_t)0x007f); + __m256i dense_mred_mask = _mm256_set1_epi8 ((uint8_t)0x7f); + __m256i dense_neg_mred_mask = _mm256_set1_epi8 ((uint8_t)0x80); + memcpy(e_align, e, N); + memcpy(utilde_align, u_tilde, N); + + /* set a register with beta, EPI16_PER_REG times */ + __m256i beta_comb = _mm256_set1_epi16 ((uint16_t) beta); + + /* Since the single-cycle shuffle acts only on 128b lanes separately, + * prepare two pairs of tables, with alternating scattered elements, to be + * broadcast-multiplied. Results are then reduced, and byte packed again + * to allow the use of the shuffle instruction */ + __m256i LUT_sparse = _mm256_set_epi16 (0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001, + 0x0001, 0x0020, 0x0008, 0x0002, + 0x0040, 0x0010, 0x0004, 0x0001); + + /* comb-multiply the lookup table made of a single register obtaining + * pre-scaled values */ + LUT_sparse = _mm256_mullo_epi16(LUT_sparse, beta_comb); + /* Vector Mersenne reduction */ + __m256i tmp = _mm256_and_si256 (LUT_sparse, mred_mask); + LUT_sparse = _mm256_srli_epi16(LUT_sparse, 7); + LUT_sparse = _mm256_add_epi16(tmp, LUT_sparse); + /*semantics from the manual call for a *byte amount* in _mm256_srli_si256*/ + tmp = _mm256_srli_si256(LUT_sparse, 7); + LUT_sparse = _mm256_or_si256(LUT_sparse, tmp); + + /* Convert restricted to rescaled values by batch exponentiating them + * via shuffle: "shuffling" the table according to the restricted values + * yields beta-multiplied elements of F_q*/ + for (int i = 0; i < ROUND_UP(N, EPI8_PER_REG) / EPI8_PER_REG; i++ ) { + __m256i e_word = _mm256_load_si256( (__m256i const *) &e_align[i * EPI8_PER_REG]); + e_word = _mm256_shuffle_epi8(LUT_sparse, e_word); + /* add to u tilde */ + __m256i utilde_word = _mm256_load_si256( (__m256i const *) &utilde_align[i * EPI8_PER_REG]); + __m256i res_word = _mm256_add_epi8(e_word, utilde_word); + /* reduce, knowing that a single out bit is the max overflow */ + tmp = _mm256_and_si256 (res_word, dense_mred_mask); + /* no _mm256_srli_epi8 available, cope with the lack hand-clearing + * all other bits before shifting */ + res_word = _mm256_srli_epi16( + _mm256_and_si256(res_word, dense_neg_mred_mask), + 7); + res_word = _mm256_add_epi8(res_word, tmp); + _mm256_store_si256 ((__m256i *) &res_align[i * EPI8_PER_REG], res_word); + + } + memcpy(res, res_align, N); +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.c new file mode 100644 index 000000000..34111b758 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.h new file mode 100644 index 000000000..37074a44f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle_tree.h new file mode 100644 index 000000000..d83aebbb5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP256SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.c new file mode 100644 index 000000000..f993512c3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.h new file mode 100644 index 000000000..9827026fd --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/parameters.h new file mode 100644 index 000000000..0ee1dc7bc --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (968) +#define W (912) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 231 +#define NUM_NODES_SEED_TREE 1938 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 61, 121, 242, 484, 968} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 4, 11, 25, 53} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 6914 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 9665 diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.c new file mode 100644 index 000000000..c1acc1632 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP256SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.h new file mode 100644 index 000000000..11ca18e6c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/set.h new file mode 100644 index 000000000..a03ce847a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sign.c new file mode 100644 index 000000000..ae55c0f3e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.c new file mode 100644 index 000000000..1e927e515 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.c @@ -0,0 +1,454 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FQ_ELEM V_tr[K][N - K], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); +} + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_zz_vec(eta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + expand_private_seed(eta, V_tr, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde[i], &CSPRNG_state); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, sigma[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_vec(sig->rsp_0[published_rsps].sigma, sigma[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + expand_public_seed(V_tr, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + /* expand eta_tilde */ + CSPRNG_zz_vec(eta_tilde, &CSPRNG_state); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*sigma is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *sigma_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fz_vec(sigma_local, sig->rsp_0[used_rsps].sigma); + memcpy(sigma_ptr, + &sig->rsp_0[used_rsps].sigma, + DENSELY_PACKED_FZ_VEC_SIZE); + is_signature_ok = is_signature_ok && + is_zz_vec_in_restr_group(sigma_local); + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.h new file mode 100644 index 000000000..3f2624b62 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t sigma[DENSELY_PACKED_FZ_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdp-256-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/api.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/api.h new file mode 100644 index 000000000..3e2aebfe3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDP256SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdp-256-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 153 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_CRYPTO_BYTES 43592 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDP256SMALL_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.c new file mode 100644 index 000000000..cb9d3374c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDP256SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.h new file mode 100644 index 000000000..b55084025 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/csprng_hash.h @@ -0,0 +1,420 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_VEC ((BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_VEC ROUND_UP(BITS_N_ZZ_CT_RNG+CORRECTION_ZZ_VEC,8)/8 + +static inline +void CSPRNG_zz_vec(FZ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZZ_CT_RNG - N*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* get */ + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/fq_arith.h new file mode 100644 index 000000000..4cbcf6796 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/fq_arith.h @@ -0,0 +1,142 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) (((x) & 0x7F) + ((x) >> 7)) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((x) ^ 0x7F) +#define FQ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7F) +#define RESTR_TO_VAL(x) ( (FQ_ELEM) (RESTR_G_TABLE >> (8*(uint64_t)(x))) ) + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.c new file mode 100644 index 000000000..96f74b4f8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.h new file mode 100644 index 000000000..976a06507 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle_tree.h new file mode 100644 index 000000000..b8ba1e4d8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDP256SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.c new file mode 100644 index 000000000..7e9beb77f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.c @@ -0,0 +1,618 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + out[i * 3 + 2] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 3] = (in[i * 8] << 5); + } else if (n_remainder == 2) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + } else if (n_remainder == 4) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + } else if (n_remainder == 6) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 3] = (in[i * 8] << 5); + out[i * 3] |= (in[i * 8 + 1] << 2); + out[i * 3] |= (in[i * 8 + 2] >> 1); + out[i * 3 + 1] = (in[i * 8 + 2] << 7); + out[i * 3 + 1] |= (in[i * 8 + 3] << 4); + out[i * 3 + 1] |= (in[i * 8 + 4] << 1); + out[i * 3 + 1] |= (in[i * 8 + 5] >> 2); + out[i * 3 + 2] = (in[i * 8 + 5] << 6); + out[i * 3 + 2] |= (in[i * 8 + 6] << 3); + } + +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + /* PQClean-edit: unused parameter */ + (void)inlen; + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +/* + * PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + out[i * 8 + 7] = ((in[i * 3 + 2]) & 0x7); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 3] >> 5); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 3] >> 5); + out[i * 8 + 1] = ((in[i * 3] >> 2) & 0x7); + out[i * 8 + 2] = ((in[i * 3] << 1) & 0x7); + out[i * 8 + 2] |= ((in[i * 3 + 1] >> 7) & 0x7); + out[i * 8 + 3] = ((in[i * 3 + 1] >> 4) & 0x7); + out[i * 8 + 4] = ((in[i * 3 + 1] >> 1) & 0x7); + out[i * 8 + 5] = ((in[i * 3 + 1] << 2) & 0x7); + out[i * 8 + 5] |= ((in[i * 3 + 2] >> 6) & 0x7); + out[i * 8 + 6] = ((in[i * 3 + 2] >> 3) & 0x7); + } + +} + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.h new file mode 100644 index 000000000..1cd3eb145 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/pack_unpack.h @@ -0,0 +1,70 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/parameters.h new file mode 100644 index 000000000..0ee1dc7bc --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/parameters.h @@ -0,0 +1,129 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (127) +#define Z ( 7) +/* single-register table representation of E, the value of g^7=1 is also + * represented to avoid exponent renormalization*/ +#define RESTR_G_TABLE ((uint64_t) (0x0140201008040201)) +#define RESTR_G_GEN 2 +#define FQ_ELEM uint8_t +#define FZ_ELEM uint8_t +#define FQ_DOUBLEPREC uint16_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (251) +#define K (150) + +#define T (968) +#define W (912) +#define POSITION_IN_FW_STRING_T uint16_t + +/******************************************************************************/ +/****************************** RSDP(G) Parameters ****************************/ +/******************************************************************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 231 +#define NUM_NODES_SEED_TREE 1938 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 61, 121, 242, 484, 968} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 4, 11, 25, 53} +#define BITS_N_ZQ_CT_RNG 1827 +#define BITS_BETA_ZQSTAR_CT_RNG 6914 +#define BITS_V_CT_RNG 106427 +#define BITS_N_ZZ_CT_RNG 979 +#define BITS_CWSTR_RNG 9665 diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/restr_arith.h new file mode 100644 index 000000000..a0824b738 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/restr_arith.h @@ -0,0 +1,77 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x07) + ((x) >> 3)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x07) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 3)) & 0x07) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.c new file mode 100644 index 000000000..3775b8281 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDP256SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.h new file mode 100644 index 000000000..d439ac18a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDP256SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/set.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/set.h new file mode 100644 index 000000000..7f826ce65 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDP 1 +#define CATEGORY_5 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDPG +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdp-256-small_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdp-256-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdp-256-small_clean/sign.c new file mode 100644 index 000000000..16ebc891a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdp-256-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDP256SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDP256SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.c new file mode 100644 index 000000000..1abff537f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.h new file mode 100644 index 000000000..5072c0fc5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/api.h new file mode 100644 index 000000000..d21da450c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdpg-128-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CRYPTO_BYTES 9236 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..716033a8e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..e0c67a478 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.c new file mode 100644 index 000000000..2aed3a18d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.h new file mode 100644 index 000000000..19bf8a778 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..41eb8fe80 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..d8c3af694 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..70efcd829 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/parameters.h new file mode 100644 index 000000000..a02a31d0b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (243) +#define W (206) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 101 +#define NUM_NODES_SEED_TREE 488 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 61, 122, 243} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 4, 10} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 2228 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 2030 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.c new file mode 100644 index 000000000..7ba617e71 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.h new file mode 100644 index 000000000..a368b013f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/set.h new file mode 100644 index 000000000..bda9f7a38 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sign.c new file mode 100644 index 000000000..06213e7ab --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.c new file mode 100644 index 000000000..70e72129d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.h new file mode 100644 index 000000000..47db5dd18 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/api.h new file mode 100644 index 000000000..fd4512711 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-128-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CRYPTO_BYTES 9236 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..df9686904 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..884bfe955 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.c new file mode 100644 index 000000000..2bf826ca3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.h new file mode 100644 index 000000000..3afb2bfd7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..a7af688fb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..83eb6bccb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..68ab32dd5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/parameters.h new file mode 100644 index 000000000..a02a31d0b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (243) +#define W (206) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 101 +#define NUM_NODES_SEED_TREE 488 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 31, 61, 122, 243} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 1, 4, 10} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 2228 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 2030 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.c new file mode 100644 index 000000000..d754aa434 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.h new file mode 100644 index 000000000..b8701fb61 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/set.h new file mode 100644 index 000000000..865ef5867 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sign.c new file mode 100644 index 000000000..e50956989 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.c new file mode 100644 index 000000000..cf1eb74e9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.c @@ -0,0 +1,584 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG128FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG128FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.h new file mode 100644 index 000000000..5b5a8de0a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/api.h new file mode 100644 index 000000000..42bd5996b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_CRYPTO_ALGNAME "cross-rsdpg-128-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_CRYPTO_BYTES 12472 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128FAST_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..d731253ae --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..4874b381a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.c new file mode 100644 index 000000000..cd216cc6f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.h new file mode 100644 index 000000000..1d4188d2b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..4603803c9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG128FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..4f02eff08 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..3b7ec0aea --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/parameters.h new file mode 100644 index 000000000..b9f6de5d5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (153) +#define W (79) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 78 +#define NUM_NODES_SEED_TREE 310 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 5, 10, 20, 39, 77, 153} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 4, 10, 22, 47, 98} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 1413 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 1264 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.c new file mode 100644 index 000000000..8fc1100cc --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.h new file mode 100644 index 000000000..4bc5ba5df --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/set.h new file mode 100644 index 000000000..5989b793a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sign.c new file mode 100644 index 000000000..13726f3e0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.c new file mode 100644 index 000000000..0e02bb7b2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.c @@ -0,0 +1,475 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.h new file mode 100644 index 000000000..bacda4663 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/api.h new file mode 100644 index 000000000..29a0a982b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-128-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_CRYPTO_BYTES 12472 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128FAST_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.c new file mode 100644 index 000000000..cc76df801 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.h new file mode 100644 index 000000000..03bbe70fb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.c new file mode 100644 index 000000000..98c77b425 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.h new file mode 100644 index 000000000..06e59a5ea --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle_tree.h new file mode 100644 index 000000000..5440acba9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG128FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.c new file mode 100644 index 000000000..0ca13a21f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.h new file mode 100644 index 000000000..432c6e2ea --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/parameters.h new file mode 100644 index 000000000..b9f6de5d5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (153) +#define W (79) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 78 +#define NUM_NODES_SEED_TREE 310 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 5, 10, 20, 39, 77, 153} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 4, 10, 22, 47, 98} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 1413 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 1264 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.c new file mode 100644 index 000000000..475d77d6d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.h new file mode 100644 index 000000000..3e6ebcd19 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/set.h new file mode 100644 index 000000000..8761d2685 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sign.c new file mode 100644 index 000000000..a73d17862 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.c new file mode 100644 index 000000000..a7a7c0c1a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.h new file mode 100644 index 000000000..27189dece --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/api.h new file mode 100644 index 000000000..8e80266d4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdpg-128-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_CRYPTO_BYTES 7956 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128SMALL_AVX2_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.c new file mode 100644 index 000000000..18669cc97 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.h new file mode 100644 index 000000000..13225a12b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.c new file mode 100644 index 000000000..25eeb444a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.h new file mode 100644 index 000000000..d229e1d6c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle_tree.h new file mode 100644 index 000000000..57f0ed4a1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG128SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.c new file mode 100644 index 000000000..6e81b87d0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.h new file mode 100644 index 000000000..f84c7e7b9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/parameters.h new file mode 100644 index 000000000..28fcd1222 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (871) +#define W (850) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 113 +#define NUM_NODES_SEED_TREE 1745 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 7, 14, 28, 55, 109, 218, 436, 871} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 1, 3, 7, 16, 35, 73, 149} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 7903 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 8468 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.c new file mode 100644 index 000000000..c36b92065 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG128SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.h new file mode 100644 index 000000000..1b2a247c6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/set.h new file mode 100644 index 000000000..6932e956e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sign.c new file mode 100644 index 000000000..fc7c4b684 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.c new file mode 100644 index 000000000..b2edcd927 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.h new file mode 100644 index 000000000..ee38ae579 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/api.h new file mode 100644 index 000000000..6e5247b0a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG128SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-128-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 32 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 54 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CRYPTO_BYTES 7956 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CRYPTO_RANDOMBYTES 16 + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.c new file mode 100644 index 000000000..ac05031ed --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG128SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.h new file mode 100644 index 000000000..841d87dd5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.c new file mode 100644 index 000000000..2b6fc113d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.h new file mode 100644 index 000000000..a9da85bd1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle_tree.h new file mode 100644 index 000000000..c064acc54 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.c new file mode 100644 index 000000000..7e4dc7845 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.h new file mode 100644 index 000000000..45c6d5a9c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/parameters.h new file mode 100644 index 000000000..28fcd1222 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (128) +#define N ( 55) +#define K ( 36) +#define M ( 25) + +#define T (871) +#define W (850) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 3 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 113 +#define NUM_NODES_SEED_TREE 1745 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 7, 14, 28, 55, 109, 218, 436, 871} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 1, 3, 7, 16, 35, 73, 149} +#define BITS_N_ZQ_CT_RNG 521 +#define BITS_BETA_ZQSTAR_CT_RNG 7903 +#define BITS_V_CT_RNG 6208 +#define BITS_W_CT_RNG 5311 +#define BITS_M_ZZ_CT_RNG 199 +#define BITS_CWSTR_RNG 8468 diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.c new file mode 100644 index 000000000..1e0bf72d8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.h new file mode 100644 index 000000000..812cc646a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG128SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/set.h new file mode 100644 index 000000000..cddb5a331 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_1 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_3 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sha3.h new file mode 100644 index 000000000..5045aaeeb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake128incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake128_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake128_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake128_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake128_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake128_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake128x4incctx +#define SHAKE_X4_INIT shake128x4_inc_init +#define SHAKE_X4_ABSORB shake128x4_inc_absorb +#define SHAKE_X4_FINALIZE shake128x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake128x4_inc_squeeze +#define SHAKE_X4_RELEASE shake128x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sign.c new file mode 100644 index 000000000..b3dccf4fb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-128-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG128SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG128SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.c new file mode 100644 index 000000000..78d96e355 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.h new file mode 100644 index 000000000..2c337dc57 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/api.h new file mode 100644 index 000000000..7990c3cec --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdpg-192-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CRYPTO_BYTES 23380 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..13e0bc49a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..42274791e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.c new file mode 100644 index 000000000..0e1be1568 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.h new file mode 100644 index 000000000..b5c0cada5 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..d374826db --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..16f5346d6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..55222b802 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/parameters.h new file mode 100644 index 000000000..543f61881 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (255) +#define W (176) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 134 +#define NUM_NODES_SEED_TREE 510 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 64, 128, 255} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 0, 0} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 2351 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 2205 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.c new file mode 100644 index 000000000..40653d932 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.h new file mode 100644 index 000000000..daee79e04 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/set.h new file mode 100644 index 000000000..d0d8a4312 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sign.c new file mode 100644 index 000000000..48273f963 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.c new file mode 100644 index 000000000..2ad69d424 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.h new file mode 100644 index 000000000..e2653bf77 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/api.h new file mode 100644 index 000000000..11d1dbf03 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-192-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CRYPTO_BYTES 23380 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..7097b532a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..f5fb444a4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.c new file mode 100644 index 000000000..f37126c43 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.h new file mode 100644 index 000000000..bd2ea3f46 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..40511eb87 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..82c4bf8a9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..cc7b897b7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/parameters.h new file mode 100644 index 000000000..543f61881 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (255) +#define W (176) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 134 +#define NUM_NODES_SEED_TREE 510 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 64, 128, 255} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 0, 0} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 2351 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 2205 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.c new file mode 100644 index 000000000..bc6c805b0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.h new file mode 100644 index 000000000..569a95a6e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/set.h new file mode 100644 index 000000000..24abe8747 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sign.c new file mode 100644 index 000000000..134466cea --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.c new file mode 100644 index 000000000..e2398fa61 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.c @@ -0,0 +1,584 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG192FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG192FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.h new file mode 100644 index 000000000..d94dd86b8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/api.h new file mode 100644 index 000000000..0b0ca6c17 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_CRYPTO_ALGNAME "cross-rsdpg-192-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_CRYPTO_BYTES 27404 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192FAST_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..b1d2f9a3d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..e7dbffc55 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.c new file mode 100644 index 000000000..5af5cdd5f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.h new file mode 100644 index 000000000..d1fc766b6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..8f61c77db --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG192FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..f3e59f457 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..6fb494f82 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/parameters.h new file mode 100644 index 000000000..159f4ae12 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (230) +#define W (123) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 119 +#define NUM_NODES_SEED_TREE 462 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 29, 58, 115, 230} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 4, 10, 23} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 2125 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 2003 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.c new file mode 100644 index 000000000..b451e672d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.h new file mode 100644 index 000000000..3de452b7e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/set.h new file mode 100644 index 000000000..ce460cbd9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sign.c new file mode 100644 index 000000000..838e8f707 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.c new file mode 100644 index 000000000..c77cc7cb2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.c @@ -0,0 +1,475 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.h new file mode 100644 index 000000000..98d79dbf0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/api.h new file mode 100644 index 000000000..09cf2c18b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-192-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_CRYPTO_BYTES 27404 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192FAST_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.c new file mode 100644 index 000000000..eeffb17f6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.h new file mode 100644 index 000000000..194e3fc37 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.c new file mode 100644 index 000000000..f961381ed --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.h new file mode 100644 index 000000000..af6c1d390 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle_tree.h new file mode 100644 index 000000000..e246ba503 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG192FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.c new file mode 100644 index 000000000..0ceba41f6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.h new file mode 100644 index 000000000..ac486b44e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/parameters.h new file mode 100644 index 000000000..159f4ae12 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (230) +#define W (123) +#define POSITION_IN_FW_STRING_T uint8_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 119 +#define NUM_NODES_SEED_TREE 462 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 29, 58, 115, 230} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 4, 10, 23} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 2125 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 2003 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.c new file mode 100644 index 000000000..0864cb6e8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.h new file mode 100644 index 000000000..d68f42c09 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/set.h new file mode 100644 index 000000000..cdb028a3f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sign.c new file mode 100644 index 000000000..5204b4291 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.c new file mode 100644 index 000000000..1c1cfc921 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.h new file mode 100644 index 000000000..8d7fbaddc --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/api.h new file mode 100644 index 000000000..13feb806f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdpg-192-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_CRYPTO_BYTES 18188 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192SMALL_AVX2_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.c new file mode 100644 index 000000000..b0cc9fca4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.h new file mode 100644 index 000000000..75aef4d2c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.c new file mode 100644 index 000000000..1faa6ee08 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.h new file mode 100644 index 000000000..27376fb9f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle_tree.h new file mode 100644 index 000000000..e4b79ff6a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG192SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.c new file mode 100644 index 000000000..d9efbd706 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.h new file mode 100644 index 000000000..402049b79 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/parameters.h new file mode 100644 index 000000000..177de0c6e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (949) +#define W (914) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 167 +#define NUM_NODES_SEED_TREE 1901 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 119, 238, 475, 949} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 16, 34, 71} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 8627 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 9373 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.c new file mode 100644 index 000000000..f916194bf --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG192SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.h new file mode 100644 index 000000000..21d85b250 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/set.h new file mode 100644 index 000000000..78bbcbb20 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sign.c new file mode 100644 index 000000000..30f50cc2e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.c new file mode 100644 index 000000000..f9ef00c56 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.h new file mode 100644 index 000000000..4c8fbbd17 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/api.h new file mode 100644 index 000000000..201933a0f --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG192SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-192-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 48 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 83 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CRYPTO_BYTES 18188 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CRYPTO_RANDOMBYTES 24 + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.c new file mode 100644 index 000000000..5af2bcdbf --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG192SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.h new file mode 100644 index 000000000..96b409f08 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.c new file mode 100644 index 000000000..711e5cec3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.h new file mode 100644 index 000000000..b603861b4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle_tree.h new file mode 100644 index 000000000..6b4f93654 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.c new file mode 100644 index 000000000..30dfba146 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.h new file mode 100644 index 000000000..095427012 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/parameters.h new file mode 100644 index 000000000..177de0c6e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/parameters.h @@ -0,0 +1,130 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (192) +#define N ( 79) +#define K ( 48) +#define M ( 40) + +#define T (949) +#define W (914) +#define POSITION_IN_FW_STRING_T uint16_t + +/********************************* Category 5 *********************************/ + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 167 +#define NUM_NODES_SEED_TREE 1901 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 15, 30, 60, 119, 238, 475, 949} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 1, 3, 7, 16, 34, 71} +#define BITS_N_ZQ_CT_RNG 751 +#define BITS_BETA_ZQSTAR_CT_RNG 8627 +#define BITS_V_CT_RNG 13483 +#define BITS_W_CT_RNG 11025 +#define BITS_M_ZZ_CT_RNG 317 +#define BITS_CWSTR_RNG 9373 diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.c new file mode 100644 index 000000000..501fd13ac --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.h new file mode 100644 index 000000000..de5e35d55 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG192SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/set.h new file mode 100644 index 000000000..66b543e0a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_3 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_5 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sign.c new file mode 100644 index 000000000..ca7232cb0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-192-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG192SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG192SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.c new file mode 100644 index 000000000..f50e55c40 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.h new file mode 100644 index 000000000..ddf37c845 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/api.h new file mode 100644 index 000000000..6f0763a1b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256BALANCED_AVX2_API_H +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CRYPTO_ALGNAME "cross-rsdpg-256-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CRYPTO_BYTES 40134 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.c new file mode 100644 index 000000000..cbd590de7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256BALANCED_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.h new file mode 100644 index 000000000..cde3e4784 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.c new file mode 100644 index 000000000..0ca4f180d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.h new file mode 100644 index 000000000..aeb7f09a6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle_tree.h new file mode 100644 index 000000000..bc20b232b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.c new file mode 100644 index 000000000..823e5e497 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.h new file mode 100644 index 000000000..11593f825 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/parameters.h new file mode 100644 index 000000000..6a1569939 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (356) +#define W (257) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 183 +#define NUM_NODES_SEED_TREE 715 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 12, 23, 45, 89, 178, 356} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 7, 16, 35, 74, 152} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 3281 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 3330 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.c new file mode 100644 index 000000000..2253ab1e2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.h new file mode 100644 index 000000000..86b1e7c66 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/set.h new file mode 100644 index 000000000..35bf878de --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sign.c new file mode 100644 index 000000000..89936bf34 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256BALANCED_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256BALANCED_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.c new file mode 100644 index 000000000..5be51479b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.h new file mode 100644 index 000000000..7a54d1933 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/api.h new file mode 100644 index 000000000..1d4a717a4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-256-balanced" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CRYPTO_BYTES 40134 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.c new file mode 100644 index 000000000..8a06687e2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.h new file mode 100644 index 000000000..e4cf7194c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.c new file mode 100644 index 000000000..2ac77c6b2 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.h new file mode 100644 index 000000000..26c14d4e1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle_tree.h new file mode 100644 index 000000000..46ae7f818 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.c new file mode 100644 index 000000000..2054fb41a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.h new file mode 100644 index 000000000..692666a21 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/parameters.h new file mode 100644 index 000000000..6a1569939 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (356) +#define W (257) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 183 +#define NUM_NODES_SEED_TREE 715 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 6, 12, 23, 45, 89, 178, 356} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 3, 7, 16, 35, 74, 152} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 3281 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 3330 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.c new file mode 100644 index 000000000..e6b46581e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.h new file mode 100644 index 000000000..d6cf2c1b8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/set.h new file mode 100644 index 000000000..b2f1687d8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define BALANCED 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef SPEED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sign.c new file mode 100644 index 000000000..3176ee875 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-balanced_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256BALANCED_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.c new file mode 100644 index 000000000..c420936e3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.c @@ -0,0 +1,584 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG256FAST_AVX2_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256FAST_AVX2_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256FAST_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG256FAST_AVX2_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.h new file mode 100644 index 000000000..826859563 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/api.h new file mode 100644 index 000000000..f6eebda03 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256FAST_AVX2_API_H +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_CRYPTO_ALGNAME "cross-rsdpg-256-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_CRYPTO_BYTES 48938 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256FAST_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.c new file mode 100644 index 000000000..767af4255 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256FAST_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.h new file mode 100644 index 000000000..c30cc648a --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.c new file mode 100644 index 000000000..f932df8b3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.h new file mode 100644 index 000000000..6ad66fb71 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle_tree.h new file mode 100644 index 000000000..842174c18 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG256FAST_AVX2_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.c new file mode 100644 index 000000000..0d41ae0d4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.h new file mode 100644 index 000000000..01eec83ce --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/parameters.h new file mode 100644 index 000000000..464b2ad27 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (306) +#define W (157) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 155 +#define NUM_NODES_SEED_TREE 616 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 5, 10, 20, 39, 77, 153, 306} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 4, 10, 22, 47, 98, 201} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 2828 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 2832 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.c new file mode 100644 index 000000000..1eec9a316 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.c @@ -0,0 +1,134 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + PAR_CSPRNG_STATE_T par_csprng_state; + CSPRNG_STATE_T single_csprng_state; + + unsigned char csprng_inputs[4][CSPRNG_INPUT_LEN]; + unsigned char csprng_outputs[4][(T / 4 + 1)*SEED_LENGTH_BYTES]; + + /* prepare the input buffer for the CSPRNG as the concatenation of: + * root_seed || salt || domain_separation_counter */ + memcpy(csprng_inputs[0], root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_inputs[0] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[0][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + /* call the CSPRNG once to generate 4 seeds */ + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + initialize_csprng(&single_csprng_state, csprng_inputs[0], CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &single_csprng_state); + csprng_release(&single_csprng_state); + + /* from the 4 seeds generale all T leaves */ + for (int i = 0; i < 4; i++) { + memcpy(csprng_inputs[i], &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* increment the domain separation counter */ + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_inputs[i][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = i + 2; + } + par_initialize_csprng(4, &par_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(4, &par_csprng_state, csprng_outputs[0], csprng_outputs[1], csprng_outputs[2], csprng_outputs[3], (T / 4 + 1)*SEED_LENGTH_BYTES); + par_csprng_release(4, &par_csprng_state); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], csprng_outputs[i], (T / 4 + remainders[i])*SEED_LENGTH_BYTES ); + offset += remainders[i]; + } + + return T; +} + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.h new file mode 100644 index 000000000..8861bfad9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256FAST_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256FAST_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/set.h new file mode 100644 index 000000000..8a3b668df --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sign.c new file mode 100644 index 000000000..3f40ff7b3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256FAST_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256FAST_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.c new file mode 100644 index 000000000..2c02da043 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.c @@ -0,0 +1,475 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + unsigned char rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_compute_round_seeds(rounds_seeds, root_seed, sig->salt); + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_compute(commit_digests[0], cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_proof_compute(sig->mtp, cmt_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_publish_round_seeds(sig->stp, rounds_seeds, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t rounds_seeds[T * SEED_LENGTH_BYTES] = {0}; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_regenerate_round_seeds(rounds_seeds, fixed_weight_b, sig->stp); + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.h new file mode 100644 index 000000000..df96f21c8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/CROSS.h @@ -0,0 +1,74 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + uint8_t stp[W * SEED_LENGTH_BYTES]; + uint8_t mtp[W * HASH_DIGEST_LENGTH]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/api.h new file mode 100644 index 000000000..b56c43b14 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256FAST_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-256-fast" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_CRYPTO_BYTES 48938 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256FAST_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.c new file mode 100644 index 000000000..5fd3aef68 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256FAST_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.h new file mode 100644 index 000000000..95c827533 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.c new file mode 100644 index 000000000..bebd656d0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.h new file mode 100644 index 000000000..3e65253d6 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle_tree.h new file mode 100644 index 000000000..a5da1bb02 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/merkle_tree.h @@ -0,0 +1,97 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + uint8_t quad_hash[4][HASH_DIGEST_LENGTH]; + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + int offset = 0; + for (int i = 0; i < 4; i++) { + hash(quad_hash[i], + leaves[(T / 4)*i + offset], + (T / 4 + remainders[i])*HASH_DIGEST_LENGTH); + offset += remainders[i]; + } + hash(root, (const unsigned char *)quad_hash, 4 * HASH_DIGEST_LENGTH); +} + +uint16_t PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_proof_compute(uint8_t mtp[W * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&mtp[HASH_DIGEST_LENGTH * published], + &leaves[i], + HASH_DIGEST_LENGTH); + published++; + } + } + return published; +} + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[W * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t published = 0; + for (int i = 0; i < T; i++) { + if (leaves_to_reveal[i] == TO_PUBLISH) { + memcpy(&recomputed_leaves[i], + &mtp[HASH_DIGEST_LENGTH * published], + HASH_DIGEST_LENGTH); + published++; + } + } + PQCLEAN_CROSSRSDPG256FAST_CLEAN_merkle_tree_root_compute(root, recomputed_leaves); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.c new file mode 100644 index 000000000..fcafffdf9 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.h new file mode 100644 index 000000000..68876e7c4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/parameters.h new file mode 100644 index 000000000..464b2ad27 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (306) +#define W (157) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 155 +#define NUM_NODES_SEED_TREE 616 +#define NODES_PER_LEVEL_ARRAY {1, 2, 3, 5, 10, 20, 39, 77, 153, 306} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 1, 4, 10, 22, 47, 98, 201} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 2828 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 2832 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.c new file mode 100644 index 000000000..7c2c15bad --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.c @@ -0,0 +1,123 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + memcpy(csprng_input, root_seed, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + /* set counter for domain separation to 1 */ + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = 0; + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = 1; + + unsigned char quad_seed[4 * SEED_LENGTH_BYTES]; + CSPRNG_STATE_T csprng_states[4]; + initialize_csprng(&csprng_states[0], csprng_input, CSPRNG_INPUT_LEN); + csprng_randombytes(quad_seed, 4 * SEED_LENGTH_BYTES, &csprng_states[0]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[0]); + + int remainders[4] = {0}; + if (T % 4 > 0) { + remainders[0] = 1; + } + if (T % 4 > 1) { + remainders[1] = 1; + } + if (T % 4 > 2) { + remainders[2] = 1; + } + + int offset = 0; + for (int i = 0; i < 4; i++) { + memcpy(csprng_input, &quad_seed[i * SEED_LENGTH_BYTES], SEED_LENGTH_BYTES); + csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] += 1; + initialize_csprng(&csprng_states[i], csprng_input, CSPRNG_INPUT_LEN); + + csprng_randombytes(&rounds_seeds[((T / 4)*i + offset)*SEED_LENGTH_BYTES], + (T / 4 + remainders[i])*SEED_LENGTH_BYTES, + &csprng_states[i]); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_states[i]); + + offset += remainders[i]; + } + return T; +} + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&seed_storage[SEED_LENGTH_BYTES * published], + &rounds_seeds[i * SEED_LENGTH_BYTES], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} + +/* simply picks seeds out of the storage and places them in the in-memory array */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage) { + int published = 0; + for (int i = 0; i < T; i++) { + if (indices_to_publish[i] == TO_PUBLISH) { + memcpy(&rounds_seeds[i * SEED_LENGTH_BYTES], + &seed_storage[SEED_LENGTH_BYTES * published], + SEED_LENGTH_BYTES); + published++; + } + } + return published; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.h new file mode 100644 index 000000000..a7a444ab3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/seedtree.h @@ -0,0 +1,44 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256FAST_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_compute_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_publish_round_seeds(unsigned char *seed_storage, + const unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T]); + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_regenerate_round_seeds(unsigned char rounds_seeds[T * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *seed_storage); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/set.h new file mode 100644 index 000000000..cc31372f8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define SPEED 1 + +#define NO_TREES 1 + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SIG_SIZE diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sign.c new file mode 100644 index 000000000..bddb77de0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-fast_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256FAST_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256FAST_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.c new file mode 100644 index 000000000..5ce10a3fb --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.c @@ -0,0 +1,589 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "architecture_detect.h" +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + fz_inf_w_by_fz_matrix(eta, zeta, W_mat_avx); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + /* enqueue the calls to hash */ + int to_hash = 0; + int round_idx_queue[4] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + to_hash++; + round_idx_queue[to_hash - 1] = i; + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash - 1], s_tilde); + + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_rsdp_g_vec(cmt_0_i_input[to_hash - 1] + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + /* Fixed endianness marshalling of round counter */ + cmt_0_i_input[to_hash - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + memcpy(cmt_1_i_input[to_hash - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + if (to_hash == 4 || i == T - 1) { + par_hash( + to_hash, + cmt_0[round_idx_queue[0]], + cmt_0[round_idx_queue[1]], + cmt_0[round_idx_queue[2]], + cmt_0[round_idx_queue[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + par_hash( + to_hash, + cmt_1[round_idx_queue[0]], + cmt_1[round_idx_queue[1]], + cmt_1[round_idx_queue[2]], + cmt_1[round_idx_queue[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash = 0; + } + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + alignas(EPI8_PER_REG) uint16_t W_mat_avx[M][ROUND_UP(N - M, EPI16_PER_REG)] = {{0}}; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + W_mat_avx[i][j] = W_mat[i][j]; + } + } + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[4][DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + + uint8_t cmt_1_i_input[4][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + + /* place the salt in the hash input for all parallel instances of keccak */ + for (int instance = 0; instance < 4; instance++) { + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input[instance] + offset_salt, sig->salt, SALT_LENGTH_BYTES); + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input[instance] + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + } + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + /* enqueue the calls to hash */ + int to_hash_cmt_1 = 0; + int to_hash_cmt_0 = 0; + int round_idx_queue_cmt_1[4] = {0}; + int round_idx_queue_cmt_0[4] = {0}; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + + /* save the index for the hash output */ + to_hash_cmt_1++; + round_idx_queue_cmt_1[to_hash_cmt_1 - 1] = i; + + memcpy(cmt_1_i_input[to_hash_cmt_1 - 1], + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[to_hash_cmt_1 - 1][SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat_avx); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + + /* save the index for the hash output */ + to_hash_cmt_0++; + round_idx_queue_cmt_0[to_hash_cmt_0 - 1] = i; + + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input[to_hash_cmt_0 - 1] + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat_avx); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn(cmt_0_i_input[to_hash_cmt_0 - 1], to_compress); + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[to_hash_cmt_0 - 1][offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + } + /* hash committment 1 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_1 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_1, + cmt_1[round_idx_queue_cmt_1[0]], + cmt_1[round_idx_queue_cmt_1[1]], + cmt_1[round_idx_queue_cmt_1[2]], + cmt_1[round_idx_queue_cmt_1[3]], + cmt_1_i_input[0], + cmt_1_i_input[1], + cmt_1_i_input[2], + cmt_1_i_input[3], + sizeof(cmt_1_i_input) / 4 + ); + to_hash_cmt_1 = 0; + } + /* hash committment 0 in batches of 4 (or less on the last round) */ + if (to_hash_cmt_0 == 4 || i == T - 1) { + par_hash( + to_hash_cmt_0, + cmt_0[round_idx_queue_cmt_0[0]], + cmt_0[round_idx_queue_cmt_0[1]], + cmt_0[round_idx_queue_cmt_0[2]], + cmt_0[round_idx_queue_cmt_0[3]], + cmt_0_i_input[0], + cmt_0_i_input[1], + cmt_0_i_input[2], + cmt_0_i_input[3], + sizeof(cmt_0_i_input) / 4 + ); + to_hash_cmt_0 = 0; + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.h new file mode 100644 index 000000000..b86d7f9e4 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/api.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/api.h new file mode 100644 index 000000000..8fe0618a1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256SMALL_AVX2_API_H +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_CRYPTO_ALGNAME "cross-rsdpg-256-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_CRYPTO_BYTES 32742 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256SMALL_AVX2_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/architecture_detect.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/architecture_detect.h new file mode 100644 index 000000000..6372cedc3 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/architecture_detect.h @@ -0,0 +1,13 @@ + +#pragma once + +/* liboqs-edit: avoid checking for ISA extensions + * when compiling avx2 just assume that Intel Instrinsics are available */ +#include +#include +#include +#include +//#include // liboqs-edit: x86intrin.h is not available on Windows + +#define EPI8_PER_REG 32 +#define EPI16_PER_REG 16 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.c new file mode 100644 index 000000000..e075a2d72 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256SMALL_AVX2_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.h new file mode 100644 index 000000000..e3f83d36d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/fq_arith.h new file mode 100644 index 000000000..f03e48133 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/fq_arith.h @@ -0,0 +1,197 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include +#include + +#include "architecture_detect.h" +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ +/* Used by keygen */ +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = RESTR_TO_VAL(e[K + i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + if (i == Q - 1) { + res_dprec[j] = FQRED_SINGLE(res_dprec[j]); + } + } + } + /* Save result trimming to regular precision */ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +/* FQ_elem is uint16_t Q is 509 */ +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + FQ_DOUBLEPREC res_dprec[N - K] = {0}; + for (int i = 0; i < N - K; i++) { + res_dprec[i] = e[K + i]; + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res_dprec[j] += FQRED_SINGLE( + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } + /* Save result trimming to regular precision. A single reduction is ok, as + * k < Q-1 = 508*/ + for (int i = 0; i < N - K; i++) { + res[i] = FQRED_SINGLE(res_dprec[i]); + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_SINGLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_SINGLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.c new file mode 100644 index 000000000..211dc6dcc --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.c @@ -0,0 +1,399 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* maximum number of parallel executions of the hash function */ +#define PAR_DEGREE 4 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* create the hash tree starting from the leaves */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + /* save the position of the hash inputs and outputs */ + to_hash++; + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} + +/* PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + + /* enqueue the calls to hash */ + int to_hash = 0; + int out_pos_queue[4] = {0}; + int in_pos_queue[4] = {0}; + + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + else { + /* at least one of the siblings is valid: there is a hash to compute */ + to_hash++; + /* save the position of the hash inputs and outputs */ + out_pos_queue[to_hash - 1] = OFFSET(PARENT(i) + layer_offsets[parent_layer]); + in_pos_queue[to_hash - 1] = OFFSET(SIBLING(i)); + + /* if the right sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(i), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* if the left sibling is invalid, copy it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + memcpy( + merkle_tree + OFFSET(SIBLING(i)), + merkle_proof + OFFSET(ctr), + HASH_DIGEST_LENGTH); + ctr++; + } + + /* set the parent node as valid */ + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + /* go up to the next tree level */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } + + /* hash in batches of 4 (or less when changing tree level) */ + if (to_hash == 4 || node_ctr == 0) { + par_hash( + to_hash, + merkle_tree + out_pos_queue[0], + merkle_tree + out_pos_queue[1], + merkle_tree + out_pos_queue[2], + merkle_tree + out_pos_queue[3], + merkle_tree + in_pos_queue[0], + merkle_tree + in_pos_queue[1], + merkle_tree + in_pos_queue[2], + merkle_tree + in_pos_queue[3], + 2 * HASH_DIGEST_LENGTH); + to_hash = 0; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.h new file mode 100644 index 000000000..e60fa4af0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle_tree.h new file mode 100644 index 000000000..16d6db317 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG256SMALL_AVX2_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.c new file mode 100644 index 000000000..2aa919a24 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.h new file mode 100644 index 000000000..ee9c49f0d --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/parameters.h new file mode 100644 index 000000000..d3a8a695e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (996) +#define W (945) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 219 +#define NUM_NODES_SEED_TREE 1994 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 63, 125, 249, 498, 996} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 1, 4, 11, 25} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 9070 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 9947 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/restr_arith.h new file mode 100644 index 000000000..67d4e9ea8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/restr_arith.h @@ -0,0 +1,138 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static inline +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + /* although W_mat is constant, it is dynamically + * expanded, ISO C11 forbids const here */ + uint16_t W_mat[M][ROUND_UP(N - M, EPI16_PER_REG)]) { + alignas(EPI8_PER_REG)FZ_DOUBLEPREC res_dprec[ROUND_UP(N - M, EPI16_PER_REG)] = {0}; + __m256i mred_mask = _mm256_set1_epi16 (0x007f); + for (int i = 0; i < M; i++) { + for (int j = 0; j < ROUND_UP(N - M, EPI16_PER_REG) / EPI16_PER_REG; j++) { + __m256i res_w = _mm256_load_si256( + (__m256i const *) &res_dprec[j * EPI16_PER_REG] ); + __m256i e_coeff = _mm256_set1_epi16(e[i]); + __m256i W_mat_slice = _mm256_lddqu_si256( + (__m256i const *) &W_mat[i][j * EPI16_PER_REG] ); + __m256i tmp = _mm256_mullo_epi16(e_coeff, W_mat_slice); + /* Vector Mersenne reduction */ + __m256i tmp2 = _mm256_and_si256 (tmp, mred_mask); + tmp = _mm256_srli_epi16(tmp, 7); + tmp = _mm256_add_epi16(tmp, tmp2); + res_w = _mm256_add_epi16(res_w, tmp); + /* store back*/ + _mm256_store_si256 ((__m256i *) &res_dprec[j * EPI16_PER_REG], res_w); + } + } + + /* Save result trimming to regular precision */ + for (int i = 0; i < N - M; i++) { + res[i] = FZRED_DOUBLE(res_dprec[i]); + } + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.c new file mode 100644 index 000000000..1d7c74b30 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.c @@ -0,0 +1,326 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* maximum number of parallel executions of the CSPRNG */ +#define PAR_DEGREE 4 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* reset left and right children */ + /* + for(int i = 0; i < PAR_DEGREE; i++){ + left_children[i] = discarded_seed; + right_children[i] = discarded_seed; + } + */ + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + to_expand++; + + father_node_idxs[to_expand - 1] = ancestors + node_in_level; + father_node_storage_idxs[to_expand - 1] = father_node_idxs[to_expand - 1] - missing_nodes_before[level]; + + /* prepare the children of node i to be expanded */ + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG256SMALL_AVX2_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + unsigned char csprng_inputs[PAR_DEGREE][CSPRNG_INPUT_LEN]; + + PAR_CSPRNG_STATE_T tree_csprng_state; + + for (int i = 0; i < PAR_DEGREE; i++) { + memcpy(csprng_inputs[i] + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + } + + uint16_t father_node_idxs[PAR_DEGREE]; + uint16_t father_node_storage_idxs[PAR_DEGREE]; + + unsigned char *left_children[PAR_DEGREE]; + unsigned char *right_children[PAR_DEGREE]; + + unsigned char discarded_seed[SEED_LENGTH_BYTES]; + + int nodes_used = 0; + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + /* enqueue the calls to the CSPRNG */ + int to_expand = 0; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + ancestors = 0; + + for (int level = 0; level <= LOG2(T); level++) { + + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + + /* skip unpublished nodes */ + if (flags_tree_to_publish[ancestors + node_in_level] == TO_PUBLISH) { + + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + + /* if the node is published and an orphan then memcpy it from the proof */ + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + + /* if the node is published and not a leaf then its children need to be expanded */ + if (level < LOG2(T)) { + to_expand++; + /* prepare the childen to be expanded */ + father_node_idxs[to_expand - 1] = father_node_idx; + father_node_storage_idxs[to_expand - 1] = father_node_storage_idx; + memcpy(csprng_inputs[to_expand - 1], seed_tree + father_node_storage_idxs[to_expand - 1]*SEED_LENGTH_BYTES, SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_inputs[to_expand - 1] + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idxs[to_expand - 1]; + left_children[to_expand - 1] = seed_tree + (LEFT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + /* the last leaf might not be needed */ + if ((RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + right_children[to_expand - 1] = seed_tree + (RIGHT_CHILD(father_node_idxs[to_expand - 1]) - missing_nodes_before[level + 1]) * SEED_LENGTH_BYTES; + } else { + right_children[to_expand - 1] = discarded_seed; + } + } + } + + /* call CSPRNG in batches of 4 (or less when changing tree level) */ + if (level < LOG2(T)) { + if (to_expand == PAR_DEGREE || (node_in_level == nodes_in_level[level] - 1)) { + par_initialize_csprng(to_expand, &tree_csprng_state, csprng_inputs[0], csprng_inputs[1], csprng_inputs[2], csprng_inputs[3], CSPRNG_INPUT_LEN); + par_csprng_randombytes(to_expand, &tree_csprng_state, left_children[0], left_children[1], left_children[2], left_children[3], SEED_LENGTH_BYTES); + par_csprng_randombytes(to_expand, &tree_csprng_state, right_children[0], right_children[1], right_children[2], right_children[3], SEED_LENGTH_BYTES); + par_csprng_release(to_expand, &tree_csprng_state); + to_expand = 0; + } + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.h new file mode 100644 index 000000000..61904e6a1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_AVX2_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/set.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/set.h new file mode 100644 index 000000000..6bbd541a1 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/set.h @@ -0,0 +1,25 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_avx2 +#define HIGH_COMPATIBILITY_X86_64 +#define HIGH_PERFORMANCE_X86_64 + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sign.c new file mode 100644 index 000000000..79c0b394b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_avx2/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256SMALL_AVX2_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256SMALL_AVX2_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.c new file mode 100644 index 000000000..9331bd012 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.c @@ -0,0 +1,480 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#include +#include + +#include "CROSS.h" +#include "csprng_hash.h" +#include "fq_arith.h" +#include "merkle_tree.h" +#include "pack_unpack.h" +#include "randombytes.h" +#include "seedtree.h" + +static +void expand_public_seed(FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]) { + CSPRNG_STATE_T CSPRNG_state_mat; + initialize_csprng(&CSPRNG_state_mat, seed_pub, KEYPAIR_SEED_LENGTH_BYTES); + + CSPRNG_fq_mat(V_tr, &CSPRNG_state_mat); + CSPRNG_fz_mat(W_mat, &CSPRNG_state_mat); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_mat); +} + +static +void expand_private_seed(FZ_ELEM eta[N], + FZ_ELEM zeta[M], + FQ_ELEM V_tr[K][N - K], + FZ_ELEM W_mat[M][N - M], + const uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]) { + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + expand_public_seed(V_tr, W_mat, seede_seed_pub[1]); + + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); +} + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK) { + /* generation of random material for public and private key */ + randombytes(SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + uint8_t seede_seed_pub[2][KEYPAIR_SEED_LENGTH_BYTES]; + + CSPRNG_STATE_T CSPRNG_state; + initialize_csprng(&CSPRNG_state, SK->seed, KEYPAIR_SEED_LENGTH_BYTES); + csprng_randombytes((uint8_t *)seede_seed_pub, + 2 * KEYPAIR_SEED_LENGTH_BYTES, + &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + memcpy(PK->seed_pub, seede_seed_pub[1], KEYPAIR_SEED_LENGTH_BYTES); + + /* expansion of matrix/matrices */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + /* expansion of secret key material */ + FZ_ELEM eta[N]; + CSPRNG_STATE_T CSPRNG_state_eta; + initialize_csprng(&CSPRNG_state_eta, seede_seed_pub[0], KEYPAIR_SEED_LENGTH_BYTES); + + FZ_ELEM zeta[M]; + CSPRNG_zz_inf_w(zeta, &CSPRNG_state_eta); + fz_inf_w_by_fz_matrix(eta, zeta, W_mat); + fz_dz_norm_sigma(eta); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state_eta); + + /* compute public syndrome */ + FQ_ELEM pub_syn[N - K]; + restr_vec_by_fq_matrix(pub_syn, eta, V_tr); + fq_dz_norm_synd(pub_syn); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn(PK->s, pub_syn); +} + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_sign(const prikey_t *const SK, + const char *const m, + const size_t mlen, + CROSS_sig_t *const sig) { + /* Wipe any residual information in the sig structure allocated by the + * caller */ + memset(sig, 0, sizeof(CROSS_sig_t)); + /* Key material expansion */ + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM eta[N]; + FZ_ELEM zeta[M]; + FZ_ELEM W_mat[M][N - M]; + expand_private_seed(eta, zeta, V_tr, W_mat, SK->seed); + + uint8_t root_seed[SEED_LENGTH_BYTES]; + randombytes(root_seed, SEED_LENGTH_BYTES); + randombytes(sig->salt, SALT_LENGTH_BYTES); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_seed_tree_from_root(seed_tree, root_seed, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + FZ_ELEM eta_tilde[T][N]; + FZ_ELEM sigma[T][N]; + FQ_ELEM u_tilde[T][N]; + FQ_ELEM s_tilde[N - K]; + + FZ_ELEM zeta_tilde[M]; + FZ_ELEM delta[T][M]; + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt ; place salt at the end */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + /* cmt_1_i_input is concat(seed,salt,round index) */ + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + CSPRNG_STATE_T CSPRNG_state; + for (uint16_t i = 0; i < T; i++) { + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, + csprng_input, + SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)); + /* expand eta_tilde */ + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + restr_inf_w_sub(delta[i], zeta, zeta_tilde); + fz_dz_norm_delta(delta[i]); + fz_inf_w_by_fz_matrix(eta_tilde[i], zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde[i]); + restr_vec_sub(sigma[i], eta, eta_tilde[i]); + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma[i]); + fz_dz_norm_sigma(sigma[i]); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde[i], &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + FQ_ELEM u[N]; + fq_vec_by_fq_vec_pointwise(u, v, u_tilde[i]); + fq_vec_by_fq_matrix(s_tilde, u, V_tr); + fq_dz_norm_synd(s_tilde); + + /* cmt_0_i_input contains s-tilde || sigma_i || salt */ + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, s_tilde); + + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_rsdp_g_vec(cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE, delta[i]); + /* Fixed endianness marshalling of round counter + * i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + } + + /* vector containing d_0 and d_1 from spec */ + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + uint8_t merkle_tree_0[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_compute(commit_digests[0], merkle_tree_0, cmt_0); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + hash(sig->digest_01, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + /* first challenge extraction */ + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + /* place d_m at the beginning of the input of the hash generating d_beta*/ + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, 2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + /* Computation of the first round of responses */ + FQ_ELEM y[T][N]; + for (int i = 0; i < T; i++) { + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde[i], + beta[i], + u_tilde[i]); + fq_dz_norm(y[i]); + } + /* y vectors are packed before being hashed */ + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + /* Second challenge extraction */ + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + hash(sig->digest_b, digest_b_buf, sizeof(digest_b_buf)); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + /* Computation of the second round of responses */ + + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_proof_compute(sig->mtp, merkle_tree_0, fixed_weight_b); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_publish_seeds(sig->stp, seed_tree, fixed_weight_b); + + int published_rsps = 0; + for (int i = 0; i < T; i++) { + if (fixed_weight_b[i] == 0) { + /* PQClean-edit: remove assertion */ + //assert(published_rsps < T-W); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec(sig->rsp_0[published_rsps].y, y[i]); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_rsdp_g_vec(sig->rsp_0[published_rsps].delta, delta[i]); + memcpy(sig->rsp_1[published_rsps], cmt_1[i], HASH_DIGEST_LENGTH); + published_rsps++; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LENGTH (SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+SIZEOF_UINT16) +//const int csprng_input_length = SALT_LENGTH_BYTES+SEED_LENGTH_BYTES+sizeof(uint16_t); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_verify(const pubkey_t *const PK, + const char *const m, + const size_t mlen, + const CROSS_sig_t *const sig) { + CSPRNG_STATE_T CSPRNG_state; + + FQ_ELEM V_tr[K][N - K]; + FZ_ELEM W_mat[M][N - M]; + expand_public_seed(V_tr, W_mat, PK->seed_pub); + + FQ_ELEM pub_syn[N - K]; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_syn(pub_syn, PK->s); + + uint8_t beta_buf[2 * HASH_DIGEST_LENGTH + SALT_LENGTH_BYTES]; + hash(beta_buf, (uint8_t *) m, mlen); + memcpy(beta_buf + HASH_DIGEST_LENGTH, sig->digest_01, HASH_DIGEST_LENGTH); + memcpy(beta_buf + 2 * HASH_DIGEST_LENGTH, sig->salt, SALT_LENGTH_BYTES); + + uint8_t d_beta[HASH_DIGEST_LENGTH]; + hash(d_beta, beta_buf, sizeof(beta_buf)); + + FQ_ELEM beta[T]; + initialize_csprng(&CSPRNG_state, d_beta, HASH_DIGEST_LENGTH); + CSPRNG_fq_vec_beta(beta, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + uint8_t fixed_weight_b[T] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_expand_digest_to_fixed_weight(fixed_weight_b, sig->digest_b); + + uint8_t seed_tree[SEED_LENGTH_BYTES * NUM_NODES_SEED_TREE] = {0}; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_regenerate_round_seeds(seed_tree, fixed_weight_b, sig->stp, sig->salt); + uint8_t *rounds_seeds = seed_tree + + SEED_LENGTH_BYTES * NUM_INNER_NODES_SEED_TREE; + + uint8_t cmt_0_i_input[DENSELY_PACKED_FQ_SYN_SIZE + + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE + + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + const int offset_salt = DENSELY_PACKED_FQ_SYN_SIZE + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE; + const int offset_round_idx = offset_salt + SALT_LENGTH_BYTES; + /* cmt_0_i_input is syndrome||sigma ||salt */ + memcpy(cmt_0_i_input + offset_salt, sig->salt, SALT_LENGTH_BYTES); + + /* cmt_1_i_input is concat(seed,salt,round index) */ + uint8_t cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + sizeof(uint16_t)]; + memcpy(cmt_1_i_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + + uint8_t cmt_0[T][HASH_DIGEST_LENGTH] = {0}; + uint8_t cmt_1[T][HASH_DIGEST_LENGTH] = {0}; + + FZ_ELEM eta_tilde[N]; + FQ_ELEM u_tilde[N]; + + FQ_ELEM y_tilde[N] = {0}; + FQ_ELEM s_tilde[N - K] = {0}; + + FQ_ELEM y[T][N]; + + int used_rsps = 0; + int is_signature_ok = 1; + for (uint16_t i = 0; i < T; i++) { + + /* i+c */ + uint16_t domain_sep_i = i + NUM_NODES_SEED_TREE; + /* i+c+dsc */ + uint16_t domain_sep_idx_hash = domain_sep_i + HASH_CSPRNG_DOMAIN_SEP_CONST; + + if (fixed_weight_b[i] == 1) { + memcpy(cmt_1_i_input, + rounds_seeds + SEED_LENGTH_BYTES * i, + SEED_LENGTH_BYTES); + + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_1_i_input[SEED_LENGTH_BYTES + SALT_LENGTH_BYTES + 1] = domain_sep_idx_hash & 0xFF; + hash(cmt_1[i], cmt_1_i_input, sizeof(cmt_1_i_input)); + + /* CSPRNG is fed with concat(seed,salt,round index) represented + * as a 2 bytes little endian unsigned integer */ + uint8_t csprng_input[CSPRNG_INPUT_LENGTH]; + memcpy(csprng_input + SEED_LENGTH_BYTES, sig->salt, SALT_LENGTH_BYTES); + memcpy(csprng_input, rounds_seeds + SEED_LENGTH_BYTES * i, SEED_LENGTH_BYTES); + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES] = (domain_sep_i >> 8) & 0xFF; + csprng_input[SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + 1] = domain_sep_i & 0xFF; + + /* expand seed[i] into seed_e and seed_u */ + initialize_csprng(&CSPRNG_state, csprng_input, CSPRNG_INPUT_LENGTH); + FZ_ELEM zeta_tilde[M]; + CSPRNG_zz_inf_w(zeta_tilde, &CSPRNG_state); + fz_inf_w_by_fz_matrix(eta_tilde, zeta_tilde, W_mat); + fz_dz_norm_sigma(eta_tilde); + /* expand u_tilde */ + CSPRNG_fq_vec(u_tilde, &CSPRNG_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&CSPRNG_state); + + fq_vec_by_restr_vec_scaled(y[i], + eta_tilde, + beta[i], + u_tilde); + fq_dz_norm(y[i]); + } else { + /* place y[i] in the buffer for later on hashing */ + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_vec(y[i], sig->rsp_0[used_rsps].y); + + FZ_ELEM sigma_local[N]; + /*delta is memcpy'ed directly into cmt_0 input buffer */ + FZ_ELEM *delta_ptr = cmt_0_i_input + DENSELY_PACKED_FQ_SYN_SIZE; + memcpy(delta_ptr, + &sig->rsp_0[used_rsps].delta, + DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE); + FZ_ELEM delta_local[M]; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_rsdp_g_vec(delta_local, sig->rsp_0[used_rsps].delta); + is_signature_ok = is_signature_ok && + is_zz_inf_w_valid(delta_local); + fz_inf_w_by_fz_matrix(sigma_local, delta_local, W_mat); + + memcpy(cmt_1[i], sig->rsp_1[used_rsps], HASH_DIGEST_LENGTH); + used_rsps++; + + FQ_ELEM v[N]; + convert_restr_vec_to_fq(v, sigma_local); + fq_vec_by_fq_vec_pointwise(y_tilde, v, y[i]); + fq_vec_by_fq_matrix(s_tilde, y_tilde, V_tr); + fq_dz_norm_synd(s_tilde); + FQ_ELEM to_compress[N - K]; + fq_synd_minus_fq_vec_scaled(to_compress, + s_tilde, + beta[i], + pub_syn); + fq_dz_norm_synd(to_compress); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn(cmt_0_i_input, to_compress); + cmt_0_i_input[offset_round_idx] = (domain_sep_idx_hash >> 8) & 0xFF; + cmt_0_i_input[offset_round_idx + 1] = domain_sep_idx_hash & 0xFF; + + hash(cmt_0[i], cmt_0_i_input, sizeof(cmt_0_i_input)); + } + } /* end for iterating on ZKID iterations */ + + /* PQClean-edit: remove assertion */ + //assert(is_signature_ok); + + uint8_t commit_digests[2][HASH_DIGEST_LENGTH]; + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_recompute(commit_digests[0], + cmt_0, + sig->mtp, + fixed_weight_b); + hash(commit_digests[1], (unsigned char *)cmt_1, sizeof(cmt_1)); + + uint8_t digest_01_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_01_recomputed, + (unsigned char *) commit_digests, + sizeof(commit_digests)); + + uint8_t digest_b_buf[T * DENSELY_PACKED_FQ_VEC_SIZE + HASH_DIGEST_LENGTH]; + for (int x = 0; x < T; x++) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec(digest_b_buf + (x * DENSELY_PACKED_FQ_VEC_SIZE), y[x]); + } + memcpy(digest_b_buf + T * DENSELY_PACKED_FQ_VEC_SIZE, d_beta, HASH_DIGEST_LENGTH); + + uint8_t digest_b_recomputed[HASH_DIGEST_LENGTH]; + hash(digest_b_recomputed, digest_b_buf, sizeof(digest_b_buf)); + + int does_digest_01_match = ( memcmp(digest_01_recomputed, + sig->digest_01, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_01_match); + + int does_digest_b_match = ( memcmp(digest_b_recomputed, + sig->digest_b, + HASH_DIGEST_LENGTH) == 0); + + /* PQClean-edit: remove assertion */ + //assert(does_digest_b_match); + + is_signature_ok = is_signature_ok && + does_digest_01_match && + does_digest_b_match; + return is_signature_ok; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.h new file mode 100644 index 000000000..a1dbc6421 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/CROSS.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include + +#include "pack_unpack.h" +#include "parameters.h" + +/* Public key: the parity check matrix is shrunk to a seed, syndrome + * represented in full */ +typedef struct { + uint8_t seed_pub[KEYPAIR_SEED_LENGTH_BYTES]; + uint8_t s[DENSELY_PACKED_FQ_SYN_SIZE]; +} pubkey_t; + +/* Private key: just a single seed*/ +typedef struct { + uint8_t seed[KEYPAIR_SEED_LENGTH_BYTES]; +} prikey_t; + +typedef struct { + uint8_t y[DENSELY_PACKED_FQ_VEC_SIZE]; + uint8_t delta[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]; +} rsp_0_t; + +/* Signature: */ +typedef struct { + uint8_t salt[SALT_LENGTH_BYTES]; + uint8_t digest_01[HASH_DIGEST_LENGTH]; + uint8_t digest_b[HASH_DIGEST_LENGTH]; + /*Seed tree paths storage*/ + uint8_t stp[TREE_NODES_TO_STORE * SEED_LENGTH_BYTES]; + /*Merkle tree proof field.*/ + uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE]; + rsp_0_t rsp_0[T - W]; + uint8_t rsp_1[T - W][HASH_DIGEST_LENGTH]; +} CROSS_sig_t; + +/* keygen cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_keygen(prikey_t *SK, + pubkey_t *PK); + +/* sign cannot fail */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_sign(const prikey_t *SK, + const char *m, + size_t mlen, + CROSS_sig_t *sig); + +/* verify returns 1 if signature is ok, 0 otherwise */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_verify(const pubkey_t *PK, + const char *m, + size_t mlen, + const CROSS_sig_t *sig); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/LICENSE b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +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. diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/api.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/api.h new file mode 100644 index 000000000..e92f34099 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/api.h @@ -0,0 +1,80 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#ifndef PQCLEAN_CROSSRSDPG256SMALL_CLEAN_API_H +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_API_H + +#pragma once + +#include +#include + +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CRYPTO_ALGNAME "cross-rsdpg-256-small" + +/* no. of bytes of the secret key */ +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CRYPTO_SECRETKEYBYTES 64 + +/* no. of bytes of the public key */ +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CRYPTO_PUBLICKEYBYTES 106 + +/* no. of bytes of overhead in a signed message */ +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CRYPTO_BYTES 32742 + +/* required bytes of input randomness */ +#define PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CRYPTO_RANDOMBYTES 32 + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, + const unsigned char *sm, + size_t smlen, + const unsigned char *pk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, + size_t *siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *sk + ); + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, + size_t siglen, + const unsigned char *m, + size_t mlen, + const unsigned char *pk + ); + +#endif diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.c new file mode 100644 index 000000000..ce0673a45 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.c @@ -0,0 +1,82 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include + +#include "csprng_hash.h" + +#define POSITION_MASK (( (uint16_t)1 << BITS_TO_REPRESENT(T-1))-1) + +/* Fisher-Yates shuffle obtaining the entire required randomness in a single + * call */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]) { + CSPRNG_STATE_T csprng_state; + initialize_csprng(&csprng_state, + (const unsigned char *) digest, + HASH_DIGEST_LENGTH); + uint8_t CSPRNG_buffer[ROUND_UP(BITS_CWSTR_RNG, 8) / 8]; + csprng_randombytes(CSPRNG_buffer, ROUND_UP(BITS_CWSTR_RNG, 8) / 8, &csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&csprng_state); + + /* initialize CW string */ + memset(fixed_weight_string, 1, W); + memset(fixed_weight_string + W, 0, T - W); + + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + int pos_in_buf = 8; + + int curr = 0; + while (curr < T) { + /* refill randomness buffer if needed */ + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /*we need to draw a number in 0... T-1-curr */ + int bits_for_pos = BITS_TO_REPRESENT(T - 1 - curr); + uint64_t pos_mask = ( (uint64_t) 1 << bits_for_pos) - 1; + uint16_t candidate_pos = (sub_buffer & pos_mask); + if (candidate_pos < T - curr) { + int dest = curr + candidate_pos; + /* the position is admissible, swap */ + uint8_t tmp = fixed_weight_string[curr]; + fixed_weight_string[curr] = fixed_weight_string[dest]; + fixed_weight_string[dest] = tmp; + curr++; + sub_buffer = sub_buffer >> bits_for_pos; + bits_in_sub_buf -= bits_for_pos; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} /* PQCLEAN_CROSSRSDPG256SMALL_CLEAN_expand_digest_to_fixed_weight */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.h new file mode 100644 index 000000000..1a5748204 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/csprng_hash.h @@ -0,0 +1,466 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ +#pragma once + +#include "parameters.h" +#include "sha3.h" + +/************************* CSPRNG ********************************/ + +#define CSPRNG_STATE_T SHAKE_STATE_STRUCT +/* initializes a CSPRNG, given the seed and a state pointer */ +static inline +void initialize_csprng(CSPRNG_STATE_T *const csprng_state, + const unsigned char *const seed, + const uint32_t seed_len_bytes) { + // the second parameter is the security level of the SHAKE instance + xof_shake_init(csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(csprng_state, seed, seed_len_bytes); + xof_shake_final(csprng_state); +} /* end initialize_csprng */ + +/* extracts xlen bytes from the CSPRNG, given the state */ +static inline +void csprng_randombytes(unsigned char *const x, + uint64_t xlen, + CSPRNG_STATE_T *const csprng_state) { + xof_shake_extract(csprng_state, x, xlen); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void csprng_release(CSPRNG_STATE_T *const csprng_state) { + xof_shake_release(csprng_state); +} + +/**************** Parallel CSPRNG (x2, x3, x4) ***********************/ + +#define CSPRNG_X2_STATE_T SHAKE_X2_STATE_STRUCT +/* CRSPRNG_x3 calls SHAKE_x4 and discards the fourth input/output */ +#define CSPRNG_X3_STATE_T SHAKE_X4_STATE_STRUCT +#define CSPRNG_X4_STATE_T SHAKE_X4_STATE_STRUCT + +/* initialize */ +static inline +void initialize_csprng_x2(CSPRNG_X2_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const uint32_t seed_len_bytes) { + xof_shake_x2_init(csprng_state); + xof_shake_x2_update(csprng_state, seed1, seed2, seed_len_bytes); + xof_shake_x2_final(csprng_state); +} +static inline +void initialize_csprng_x3(CSPRNG_X3_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const uint32_t seed_len_bytes) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed3, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +static inline +void initialize_csprng_x4(CSPRNG_X4_STATE_T *const csprng_state, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + xof_shake_x4_init(csprng_state); + xof_shake_x4_update(csprng_state, seed1, seed2, seed3, seed4, seed_len_bytes); + xof_shake_x4_final(csprng_state); +} +/* randombytes */ +static inline +void csprng_randombytes_x2(unsigned char *const x1, unsigned char *const x2, uint64_t xlen, CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_extract(csprng_state, x1, x2, xlen); +} +static inline +void csprng_randombytes_x3(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, uint64_t xlen, CSPRNG_X3_STATE_T *const csprng_state) { + /* to perform xof_x3 it's faster to call xof_x4 and discard the last output */ + xof_shake_x4_extract(csprng_state, x1, x2, x3, x3, xlen); +} +static inline +void csprng_randombytes_x4(unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen, CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_extract(csprng_state, x1, x2, x3, x4, xlen); +} +/* release */ +static inline +void csprng_release_x2(CSPRNG_X2_STATE_T *const csprng_state) { + xof_shake_x2_release(csprng_state); +} +static inline +void csprng_release_x3(CSPRNG_X3_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} +static inline +void csprng_release_x4(CSPRNG_X4_STATE_T *const csprng_state) { + xof_shake_x4_release(csprng_state); +} + +/************** Single API for Parallel CSPRNG *******************/ + +#define PAR_CSPRNG_STATE_T par_shake_ctx + +static inline +void par_initialize_csprng(int par_level, PAR_CSPRNG_STATE_T *const states, const unsigned char *const seed1, const unsigned char *const seed2, const unsigned char *const seed3, const unsigned char *const seed4, const uint32_t seed_len_bytes) { + if (par_level == 1) { + initialize_csprng(&(states->state1), seed1, seed_len_bytes); + } else if (par_level == 2) { + initialize_csprng_x2(&(states->state2), seed1, seed2, seed_len_bytes); + } else if (par_level == 3) { + initialize_csprng_x3(&(states->state4), seed1, seed2, seed3, seed_len_bytes); + } else if (par_level == 4) { + initialize_csprng_x4(&(states->state4), seed1, seed2, seed3, seed4, seed_len_bytes); + } +} +static inline +void par_csprng_randombytes(int par_level, PAR_CSPRNG_STATE_T *const states, unsigned char *const x1, unsigned char *const x2, unsigned char *const x3, unsigned char *const x4, uint64_t xlen) { + if (par_level == 1) { + csprng_randombytes(x1, xlen, &(states->state1)); + } else if (par_level == 2) { + csprng_randombytes_x2(x1, x2, xlen, &(states->state2)); + } else if (par_level == 3) { + csprng_randombytes_x3(x1, x2, x3, xlen, &(states->state4)); + } else if (par_level == 4) { + csprng_randombytes_x4(x1, x2, x3, x4, xlen, &(states->state4)); + } +} +static inline +void par_csprng_release(int par_level, PAR_CSPRNG_STATE_T *const states) { + if (par_level == 1) { + csprng_release(&(states->state1)); + } else if (par_level == 2) { + csprng_release_x2(&(states->state2)); + } else if (par_level == 3) { + csprng_release_x3(&(states->state4)); + } else if (par_level == 4) { + csprng_release_x4(&(states->state4)); + } +} + +/******************************************************************************/ + +/* PQClean-edit: remove randombytes definition here to use PQClean randombytes */ +//static inline void randombytes(unsigned char * x, uint64_t xlen) ... + +/************************* HASH functions ********************************/ + +/* Opaque algorithm agnostic hash call */ +static inline +void hash(uint8_t digest[HASH_DIGEST_LENGTH], + const unsigned char *const m, + const uint64_t mlen) { + /* SHAKE with a 2*lambda bit digest is employed also for hashing */ + CSPRNG_STATE_T csprng_state; + xof_shake_init(&csprng_state, SEED_LENGTH_BYTES * 8); + xof_shake_update(&csprng_state, m, mlen); + xof_shake_final(&csprng_state); + xof_shake_extract(&csprng_state, digest, HASH_DIGEST_LENGTH); + /* PQClean-edit: CSPRNG release context */ + xof_shake_release(&csprng_state); +} + +#define par_xof_input par_initialize_csprng +#define par_xof_output par_csprng_randombytes +#define par_xof_release par_csprng_release + +static inline +void par_hash( + int par_level, + uint8_t digest_1[HASH_DIGEST_LENGTH], + uint8_t digest_2[HASH_DIGEST_LENGTH], + uint8_t digest_3[HASH_DIGEST_LENGTH], + uint8_t digest_4[HASH_DIGEST_LENGTH], + const unsigned char *const m_1, + const unsigned char *const m_2, + const unsigned char *const m_3, + const unsigned char *const m_4, + const uint64_t mlen) { + PAR_CSPRNG_STATE_T states; + par_xof_input(par_level, &states, m_1, m_2, m_3, m_4, mlen); + par_xof_output(par_level, &states, digest_1, digest_2, digest_3, digest_4, HASH_DIGEST_LENGTH); + par_xof_release(par_level, &states); +} + +/********************** CSPRNG Sampling functions helpers ********************/ + +static inline +FQ_ELEM fq_star_rnd_state(CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_TO_REPRESENT(Q - 2) ) - 1; + FQ_ELEM rnd_value; + do { + csprng_randombytes((unsigned char *) &rnd_value, + sizeof(FQ_ELEM), + csprng_state); + rnd_value = mask & rnd_value; + } while (rnd_value > Q - 2); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(csprng_state); + + return rnd_value + 1; +} /* end fq_star_rnd_state */ + +/***************** Specialized CSPRNGs for non binary domains *****************/ + +/* CSPRNG sampling fixed weight strings */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_expand_digest_to_fixed_weight(uint8_t fixed_weight_string[T], + const uint8_t digest[HASH_DIGEST_LENGTH]); + +#define BITS_FOR_Q BITS_TO_REPRESENT(Q-1) +#define BITS_FOR_Z BITS_TO_REPRESENT(Z-1) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC ((BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_VEC (ROUND_UP(BITS_N_ZQ_CT_RNG+CORRECTION_FQ_VEC,8)/8) + +static inline +void CSPRNG_fq_vec(FQ_ELEM res[N], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + /* NOTE: Current bit cost estimation technique underestimates the failures + * whenever they appear in a run; an upper bound on the bit-cost is considering + * the failures to be discarding the entire value, instead of a single bit. + * The following correction factor takes this into account */ + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_N_ZQ_CT_RNG - N*BITS_FOR_Q)* (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_N_ZQ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right, shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < N) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +#define BITS_FOR_Q_M_ONE BITS_TO_REPRESENT(Q-2) + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_VEC_BETA ((BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1)) +#define BUFSIZE_FQ_VEC_BETA (ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+CORRECTION_FQ_VEC_BETA,8)/8) + +static inline +void CSPRNG_fq_vec_beta(FQ_ELEM res[T], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q_M_ONE) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_BETA_ZQSTAR_CT_RNG - T*BITS_FOR_Q_M_ONE) * (BITS_FOR_Q_M_ONE-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_BETA_ZQSTAR_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_VEC_BETA]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_VEC_BETA, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < T) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + /* draw from 0 ... Q-2, then add 1*/ + res[placed] = (sub_buffer & mask) + 1; + if (res[placed] < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q_M_ONE; + bits_in_sub_buf -= BITS_FOR_Q_M_ONE; + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FQ_MAT ((BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1)) +#define BUFSIZE_FQ_MAT (ROUND_UP(BITS_V_CT_RNG+CORRECTION_FQ_MAT,8)/8) + +static inline +void CSPRNG_fq_mat(FQ_ELEM res[K][N - K], + CSPRNG_STATE_T *const csprng_state) { + const FQ_ELEM mask = ( (FQ_ELEM) 1 << BITS_FOR_Q) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_V_CT_RNG - K*(N-K)*BITS_FOR_Q) * (BITS_FOR_Q-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_V_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FQ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FQ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + + while (placed < K * (N - K)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *( (FQ_ELEM *)res + placed) = sub_buffer & mask; + if (*( (FQ_ELEM *)res + placed) < Q) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Q; + bits_in_sub_buf -= BITS_FOR_Q; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_ZZ_INF_W ((BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_ZZ_INF_W (ROUND_UP(BITS_M_ZZ_CT_RNG+CORRECTION_ZZ_INF_W,8)/8) + +static inline +void CSPRNG_zz_inf_w(FZ_ELEM res[M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_M_ZZ_CT_RNG - M*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_M_ZZ_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_ZZ_INF_W]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_ZZ_INF_W, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + res[placed] = sub_buffer & mask; + if (res[placed] < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} + +/* PQClean-edit: avoid VLA */ +#define CORRECTION_FZ_MAT ((BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1)) +#define BUFSIZE_FZ_MAT (ROUND_UP(BITS_W_CT_RNG+CORRECTION_FZ_MAT,8)/8) + +static inline +void CSPRNG_fz_mat(FZ_ELEM res[M][N - M], + CSPRNG_STATE_T *const csprng_state) { + const FZ_ELEM mask = ( (FZ_ELEM) 1 << BITS_FOR_Z) - 1; + + /* PQClean-edit: avoid VLA */ + //uint32_t correction_bit_len = (BITS_W_CT_RNG - M*(N-M)*BITS_FOR_Z) * (BITS_FOR_Z-1); + //uint32_t CSPRNG_buffer_size = ROUND_UP(BITS_W_CT_RNG+correction_bit_len,8)/8; + //uint8_t CSPRNG_buffer[ROUND_UP(CSPRNG_buffer_size,4)]; + uint8_t CSPRNG_buffer[BUFSIZE_FZ_MAT]; + + /* To facilitate hardware implementations, the uint64_t + * sub-buffer is consumed starting from the least significant byte + * i.e., from the first being output by SHAKE. Bits in the byte are + * discarded shifting them out to the right , shifting fresh ones + * in from the left end */ + csprng_randombytes(CSPRNG_buffer, BUFSIZE_FZ_MAT, csprng_state); + int placed = 0; + uint64_t sub_buffer = *(uint64_t *)CSPRNG_buffer; + int bits_in_sub_buf = 64; + /* position of the next fresh byte in CSPRNG_buffer*/ + int pos_in_buf = 8; + while (placed < M * (N - M)) { + if (bits_in_sub_buf <= 32) { + /* get 32 fresh bits from main buffer with a single load */ + uint32_t refresh_buf = *(uint32_t *) (CSPRNG_buffer + pos_in_buf); + pos_in_buf += 4; + sub_buffer |= ((uint64_t) refresh_buf) << bits_in_sub_buf; + bits_in_sub_buf += 32; + } + *((FZ_ELEM *)res + placed) = sub_buffer & mask; + if (*((FZ_ELEM *)res + placed) < Z) { + placed++; + sub_buffer = sub_buffer >> BITS_FOR_Z; + bits_in_sub_buf -= BITS_FOR_Z; + + } else { + sub_buffer = sub_buffer >> 1; + bits_in_sub_buf -= 1; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/fq_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/fq_arith.h new file mode 100644 index 000000000..bdaff5a4b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/fq_arith.h @@ -0,0 +1,177 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "csprng_hash.h" +#include "parameters.h" +#include "restr_arith.h" + +#define NUM_BITS_Q (BITS_TO_REPRESENT(Q)) + +#define FQRED_SINGLE(x) ((x)% Q) +#define FQRED_DOUBLE(x) FQRED_SINGLE(FQRED_SINGLE(x)) +#define FQRED_OPPOSITE(x) ((Q-(x)) % Q) +/* no redundant zero notation in F_509 */ +#define FQ_DOUBLE_ZERO_NORM(x) (x) + +/* for i in [0,1,2,4,8,16,32,64] RESTR_G_GEN**i mod 509 yields + * [1, 16, 256, 384, 355, 302, 93, 505] + * the following is a precomputed-squares S&M, to be optimized into muxed + * register stored tables */ + +#define RESTR_G_GEN_1 ((FQ_ELEM)RESTR_G_GEN) +#define RESTR_G_GEN_2 ((FQ_ELEM) 256) +#define RESTR_G_GEN_4 ((FQ_ELEM) 384) +#define RESTR_G_GEN_8 ((FQ_ELEM) 355) +#define RESTR_G_GEN_16 ((FQ_ELEM) 302) +#define RESTR_G_GEN_32 ((FQ_ELEM) 93) +#define RESTR_G_GEN_64 ((FQ_ELEM) 505) + +#define FQ_ELEM_CMOV(BIT,TRUE_V,FALSE_V) ( (((FQ_ELEM)0 - (BIT)) & (TRUE_V)) | (~((FQ_ELEM)0 - (BIT)) & (FALSE_V)) ) + +/* log reduction, constant time unrolled S&M w/precomputed squares. + * To be further optimized with muxed register-fitting tables */ +static inline +FQ_ELEM RESTR_TO_VAL(FQ_ELEM x) { + FQ_ELEM res1, res2, res3, res4; + res1 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 0) & 1), RESTR_G_GEN_1, 1)) * + ( FQ_ELEM_CMOV(((x >> 1) & 1), RESTR_G_GEN_2, 1)) ); + res2 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 2) & 1), RESTR_G_GEN_4, 1)) * + ( FQ_ELEM_CMOV(((x >> 3) & 1), RESTR_G_GEN_8, 1)) ); + res3 = FQRED_SINGLE( + ( FQ_ELEM_CMOV(((x >> 4) & 1), RESTR_G_GEN_16, 1)) * + ( FQ_ELEM_CMOV(((x >> 5) & 1), RESTR_G_GEN_32, 1)) ); + res4 = FQ_ELEM_CMOV(((x >> 6) & 1), RESTR_G_GEN_64, 1); + + return FQRED_SINGLE( FQRED_SINGLE(res1 * res2) * + FQRED_SINGLE(res3 * res4) ); +} + +/* in-place normalization of redundant zero representation for syndromes*/ +static inline +void fq_dz_norm_synd(FQ_ELEM v[N - K]) { + for (int i = 0; i < N - K; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} + +static inline +void fq_dz_norm(FQ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FQ_DOUBLE_ZERO_NORM(v[i]); + } +} +/* computes the product e*H of an n-element restricted vector by a (n-k)*n + * F_q H is in systematic form. Only the non systematic portion of H =[V I], + * V, is provided, transposed, hence linearized by columns so that syndrome + * computation is vectorizable. */ + +static +void restr_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FZ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + for (int i = K ; i < N; i++) { + res[i - K] = RESTR_TO_VAL(e[i]); + } + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static +void fq_vec_by_fq_matrix(FQ_ELEM res[N - K], + const FQ_ELEM e[N], + FQ_ELEM V_tr[K][N - K]) { + memcpy(res, e + K, (N - K)*sizeof(FQ_ELEM)); + for (int i = 0; i < K; i++) { + for (int j = 0; j < N - K; j++) { + res[j] = FQRED_DOUBLE( (FQ_DOUBLEPREC) res[j] + + (FQ_DOUBLEPREC) e[i] * + (FQ_DOUBLEPREC) V_tr[i][j]); + } + } +} + +static inline +void fq_vec_by_fq_vec_pointwise(FQ_ELEM res[N], + const FQ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) in1[i] * + (FQ_DOUBLEPREC) in2[i] ); + } +} + +static inline +void restr_by_fq_vec_pointwise(FQ_ELEM res[N], + const FZ_ELEM in1[N], + const FQ_ELEM in2[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) RESTR_TO_VAL(in1[i]) * + (FQ_DOUBLEPREC) in2[i]); + } +} + +/* e*beta + u_tilde*/ +static inline +void fq_vec_by_restr_vec_scaled(FQ_ELEM res[N], + const FZ_ELEM e[N], + const FQ_ELEM beta, + const FQ_ELEM u_tilde[N]) { + for (int i = 0; i < N; i++) { + res[i] = FQRED_DOUBLE( (FQ_DOUBLEPREC) u_tilde[i] + + (FQ_DOUBLEPREC) RESTR_TO_VAL(e[i]) * (FQ_DOUBLEPREC) beta) ; + } +} + +static inline +void fq_synd_minus_fq_vec_scaled(FQ_ELEM res[N - K], + const FQ_ELEM synd[N - K], + const FQ_ELEM beta, + const FQ_ELEM s[N - K]) { + for (int j = 0; j < N - K; j++) { + FQ_ELEM tmp = FQRED_DOUBLE( (FQ_DOUBLEPREC) s[j] * (FQ_DOUBLEPREC) beta); + tmp = FQ_DOUBLE_ZERO_NORM(tmp); + res[j] = FQRED_SINGLE( (FQ_DOUBLEPREC) synd[j] + FQRED_OPPOSITE(tmp) ); + } +} + +static inline +void convert_restr_vec_to_fq(FQ_ELEM res[N], + const FZ_ELEM in[N]) { + for (int j = 0; j < N; j++) { + res[j] = RESTR_TO_VAL(in[j]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.c new file mode 100644 index 000000000..c45cc9f07 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.c @@ -0,0 +1,344 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "merkle.h" + +#define LEAVES_FULL_TREE(L) ( (1UL << LOG2(L) ) ) +#define LEAVES_HALF_TREE(L) ( (LEAVES_FULL_TREE(L) >> 1) ) + +#define PARENT(i) ( ((i)%2) ? (((i)-1)/2) : (((i)-2)/2) ) +#define RIGHT_CHILD(i) ( (2*(i)+2) ) +#define LEFT_CHILD(i) ( (2*(i)+1) ) +#define SIBLING(i) ( ((i)%2) ? (i)+1 : (i)-1 ) + +#define RL(i) ((i)==1 ? r_node : l_node) +#define OFFSET(i) ( (i)*HASH_DIGEST_LENGTH ) + +#define CHALLENGE_PROOF_VALUE 0 +#define INVALID_MERKLE_NODE 0 +#define VALID_MERKLE_NODE 1 + +#define NOT_COMPUTED 0 +#define COMPUTED 1 + +/* + * setup_tree() + * + * uint16_t layer_offset[LOG2(T)+1] : Stores one offset per layer for layer change. + * Required for the computation of PARENT and CHILD nodes. + * uint16_t nodes_per_layer[LOG2(T)+1] : Stores the numbers of nodes used in the truncated Merkle tree. + */ +static +void setup_tree(uint16_t layer_offsets[LOG2(T) + 1], + uint16_t nodes_per_layer[LOG2(T) + 1]) { + uint32_t depth, layer; + uint32_t r_leaves; + int subtree_found; + + /* Initialize array with full node counts */ + for (size_t i = 0; i < LOG2(T) + 1; i++) { + layer_offsets[i] = (1UL << i); + } + + /* Count root node */ + layer = 0; + layer_offsets[layer] -= 1; + + /* Count left tree nodes (always full) */ + for (size_t i = 1; i < LOG2(T) + 1; i++) { + layer_offsets[i] -= (1UL << (i - 1)); + } + + /* Check every full subtree on right side and subtract missing nodes */ + r_leaves = T - (1UL << (LOG2(T) - 1)); + layer = 1; + while (r_leaves > 0) { + depth = 0; + subtree_found = 0; + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (int i = depth; i > 0; i--) { + layer_offsets[layer + i] -= (1UL << (i - 1)); + } + r_leaves -= LEAVES_HALF_TREE(r_leaves); + layer_offsets[layer] -= 1; + layer++; + subtree_found = 1; + } else { + depth++; + } + } + } + + /* For the offset, subtract all missing nodes from previous layers from current layer */ + for (int i = LOG2(T); i >= 0; i--) { + nodes_per_layer[i] = (1UL << i) - layer_offsets[i]; + for (int j = i - 1; j >= 0; j--) { + layer_offsets[i] -= layer_offsets[j]; + } + layer_offsets[i] >>= 1; + } +} + +/* + * get_leaf_indices() is quite similar to setup_tree(), however requires the + * offset values to compute the correct indices. + * + * uint16_t merkle_leaf_indices[T] : Stores the indices in the truncated tree + * where the leaves are placed. + * uint16_t layer_offsets[LOG2(T)+1] : Same as above. + */ +static +void get_leaf_indices(uint16_t merkle_leaf_indices[T], + const uint16_t layer_offsets[LOG2(T) + 1]) { + uint32_t r_leaves; + uint32_t idx_ctr = 0; + + /* r_node: current root node of next subtree, will always be right-child of previous root */ + /* l_node: traverses from current root node to left-childs until depth of subtree is found */ + uint32_t r_node, l_node; + uint32_t layer, depth, subtree_found; + + /* If tree is already balanced, simply copy leaves to corresponding position */ + if (T == (1UL << LOG2(T))) { + for (size_t i = 0; i < T; i++) { + merkle_leaf_indices[i] = T - 1 + i; + } + return; + } + + /* Create (un-) balanced Merkle tree */ + r_leaves = T; + depth = 0; + layer = 0; + r_node = 0; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer + depth]; + while (r_leaves > 0) { + depth = 1; + subtree_found = 0; + /* Start from the current root node r_node until the size of a full left-subtree is found. */ + /* If only one leaf is remaining, put it to current root-node, macro RL() is used to decide that. */ + while ( !subtree_found ) { + if (r_leaves <= (1UL << depth)) { + for (size_t j = 0; j < LEAVES_HALF_TREE(r_leaves); j++) { + merkle_leaf_indices[idx_ctr++] = RL(r_leaves) + j; + } + r_node = RIGHT_CHILD(r_node) - 2 * layer_offsets[layer]; + l_node = LEFT_CHILD(r_node) - 2 * layer_offsets[layer]; + layer++; + r_leaves -= LEAVES_HALF_TREE(r_leaves); + subtree_found = 1; + } else { + l_node = LEFT_CHILD(l_node) - 2 * layer_offsets[layer + depth]; + depth++; + } + } + } +} + +/* PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH] : + * stores the hashes of the associated tree nodes. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Contains the + * hashed commitments that build the tree. + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * + HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]) { + size_t i; + uint32_t node_ctr, parent_layer; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + /* Move leafs in correct positions of the unbalanced Merkle tree */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Place commitments on the leaves indicated by merkle_leaf_indices */ + for (i = 0; i < T; i++) { + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, + commitments + i, + HASH_DIGEST_LENGTH); + } + + /* Hash the child nodes */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), merkle_tree + OFFSET(SIBLING(i)), 2 * HASH_DIGEST_LENGTH); + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_proof() + * + * uint16_t merkle_proof_indices[TREE_NODES_TO_STORE] : stores the sorted indices required for the proof. + * uint16_t merkle_proof_len : Actual length of the proof. Can vary depending on the challenge. + * const unsigned char challenge : Challenge that indicated which nodes will be recomputed by the verifier. + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]) { + unsigned char flag_tree[NUM_NODES_MERKLE_TREE] = {NOT_COMPUTED}; + uint32_t node_ctr, parent_layer; + size_t i; + + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + uint16_t merkle_leaf_indices[T]; + + /* Setup the tree to get offsets for the computation of PARENT/CHILD nodes, as well as the number of nodes per layer */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Use challenges to mark nodes of path tree */ + for (size_t j = 0; j < T; j++) { + if (challenge[j] == CHALLENGE_PROOF_VALUE) { + flag_tree[merkle_leaf_indices[j]] = COMPUTED; + } + } + + /* Loop over all nodes, starting at the leaves */ + /* If at least one sibling is marked as COMPUTED, also mark the PARENT as such */ + /* Only add sibling of COMPUTED sibling as proof node if not both of them are marked as COMPUTED. */ + node_ctr = 0; + parent_layer = LOG2(T) - 1; + *merkle_proof_len = 0; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + flag_tree[PARENT(i) + layer_offsets[parent_layer]] = (flag_tree[i] == COMPUTED) || (flag_tree[SIBLING(i)] == COMPUTED); + + /* Add left sibling only if left one was computed */ + if ( (flag_tree[i] == COMPUTED) && (flag_tree[SIBLING(i)] == NOT_COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = SIBLING(i); + } + + /* Add right sibling only right was computed */ + if ( (flag_tree[i] == NOT_COMPUTED) && (flag_tree[SIBLING(i)] == COMPUTED) ) { + merkle_proof_indices[(*merkle_proof_len)++] = i; + } + + /* Due to the unbalenced structure we got to keep track of the nodes per layer processed */ + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_rebuild_merkle_tree() + * + * unsigned char merkle_tree[NUM_NODES_MERKLE_TREE*HASH_DIGEST_LENGTH] : Stores the Hashes of the recomputed Merkle tree. + * const unsigned char merkle_proof[TREE_NODES_TO_STORE] : Merkle proof containing the nodes required for recomputation. + * const unsigned char commitments[T][HASH_DIGEST_LENGTH] : Stores the commitments. + * const unsigned char challenge[T] : Challenge vector to indicate the computed commitments. + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]) { + uint16_t flag_tree_valid[NUM_NODES_MERKLE_TREE] = {INVALID_MERKLE_NODE}; + + uint16_t merkle_leaf_indices[T]; + uint16_t layer_offsets[LOG2(T) + 1]; + uint16_t nodes_per_layer[LOG2(T) + 1]; + + uint16_t ctr; + uint32_t node_ctr, parent_layer; + size_t i; + + /* Input consists of hash digests stored at child nodes and the index of the parent node for domain separation */ + unsigned char hash_input[2 * HASH_DIGEST_LENGTH]; + + /* Move leafs in correct positions of binary merkle tree */ + /* Setup the tree again, computing the offsets and from that, the leaf indices */ + setup_tree(layer_offsets, nodes_per_layer); + get_leaf_indices(merkle_leaf_indices, layer_offsets); + + /* Copy the commitments to the positions indicated by the challenge */ + for (i = 0; i < T; i++) { + if (challenge[i] == CHALLENGE_PROOF_VALUE) { + flag_tree_valid[merkle_leaf_indices[i]] = VALID_MERKLE_NODE; + memcpy(merkle_tree + merkle_leaf_indices[i]*HASH_DIGEST_LENGTH, commitments + i, HASH_DIGEST_LENGTH); + } + } + /* Create hash tree by hashing valid leaf nodes */ + ctr = 0; + node_ctr = 0; + parent_layer = LOG2(T) - 1; + for (i = NUM_NODES_MERKLE_TREE - 1; i > 0; i -= 2) { + + /* Both siblings are unused, but it must be kept track of the node and layer counter to chose the right offsets */ + if (flag_tree_valid[i] == INVALID_MERKLE_NODE && flag_tree_valid[SIBLING(i)] == INVALID_MERKLE_NODE) { + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + continue; + } + + /* Prepare input to hash */ + /* Process right sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[i] == VALID_MERKLE_NODE) { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_tree + OFFSET(i), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input + HASH_DIGEST_LENGTH, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Process left sibling from the tree if valid, otherwise take it from the merkle proof */ + if (flag_tree_valid[SIBLING(i)] == VALID_MERKLE_NODE) { + memcpy(hash_input, merkle_tree + OFFSET(SIBLING(i)), HASH_DIGEST_LENGTH); + } else { + memcpy(hash_input, merkle_proof + OFFSET(ctr), HASH_DIGEST_LENGTH); + ctr++; + } + + /* Hash it and store the digest at the parent node */ + hash(merkle_tree + OFFSET(PARENT(i) + layer_offsets[parent_layer]), hash_input, 2 * HASH_DIGEST_LENGTH); + flag_tree_valid[PARENT(i) + layer_offsets[parent_layer]] = VALID_MERKLE_NODE; + + if (node_ctr >= (uint32_t) nodes_per_layer[parent_layer + 1] - 2) { + parent_layer--; + node_ctr = 0; + } else { + node_ctr += 2; + } + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.h new file mode 100644 index 000000000..726213927 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle.h @@ -0,0 +1,41 @@ +/* + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (May 2023) + * + * @author: Patrick Karl + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_proof(uint16_t merkle_proof_indices[TREE_NODES_TO_STORE], + uint16_t *merkle_proof_len, + const unsigned char challenge[T]); + +/***********************************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_rebuild_merkle_tree(unsigned char merkle_tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const unsigned char merkle_proof[TREE_NODES_TO_STORE * HASH_DIGEST_LENGTH], + unsigned char commitments[T][HASH_DIGEST_LENGTH], + const unsigned char challenge[T]); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle_tree.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle_tree.h new file mode 100644 index 000000000..d7999ebd7 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/merkle_tree.h @@ -0,0 +1,83 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author: Patrick Karl + * @author Gerardo Pelosi + * + * Authors listed in lexicographic order. + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + uint8_t leaves[T][HASH_DIGEST_LENGTH]); +uint16_t PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]); +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]); + +#include "merkle.h" +/* Stub of the interface to Merkle tree root computer from all leaves */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_compute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + /* input, although mutable in caller, having as const is non + * tolerated in strict ISO C */ + uint8_t leaves[T][HASH_DIGEST_LENGTH]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_tree(tree, leaves); + /* Root is at first position of the tree */ + memcpy(root, tree, HASH_DIGEST_LENGTH); +} + +/* Stub interface to the function computing the Merkle tree proof, storing it + * in the signature. Returns the number of digests in the merkle tree proof */ +uint16_t PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_proof_compute(uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH], + const uint8_t leaves_to_reveal[T]) { + uint16_t mtp_len; + uint16_t merkle_proof_indices[TREE_NODES_TO_STORE]; + + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_merkle_proof(merkle_proof_indices, &mtp_len, leaves_to_reveal); + + for (size_t i = 0; i < mtp_len; i++) { + memcpy(mtp + i * HASH_DIGEST_LENGTH, tree + merkle_proof_indices[i]*HASH_DIGEST_LENGTH, + HASH_DIGEST_LENGTH); + } + return mtp_len; +} + +/* stub of the interface to Merkle tree recomputation given the proof and + * the computed leaves */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_merkle_tree_root_recompute(uint8_t root[HASH_DIGEST_LENGTH], + uint8_t recomputed_leaves[T][HASH_DIGEST_LENGTH], + const uint8_t mtp[HASH_DIGEST_LENGTH * TREE_NODES_TO_STORE], + const uint8_t leaves_to_reveal[T]) { + + unsigned char tree[NUM_NODES_MERKLE_TREE * HASH_DIGEST_LENGTH]; + + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_rebuild_merkle_tree(tree, mtp, recomputed_leaves, leaves_to_reveal); + memcpy(root, tree, HASH_DIGEST_LENGTH); +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.c new file mode 100644 index 000000000..8a700ed4c --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.c @@ -0,0 +1,649 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include +#include + +#include "pack_unpack.h" + +/*Implementation of packing of 1 to 16 bits value vectors in 8 bit vectors, + * generic inputs from 1 to 16 bit possible*/ + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec() + * + * uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn() + * + * uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE] : FQ packed in bytes + * const FQ_ELEM in[N-K] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fq(out, in, DENSELY_PACKED_FQ_SYN_SIZE, N - K); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE] : FQ packed in bytes + * const FZ_ELEM in[N] : FQ Vec input, to be packed + * + * This function handles the packing of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_VEC_SIZE, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_rsdp_g_vec() + * + * uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE] : Zz packed in bytes + * const FZ_ELEM in[M] : Zz Vec input, to be packed + * + * This function handles the packing of the add. rdsp(g) vector in Zz + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fz(out, in, DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE, M); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fq() + * + * uint8_t *out : FQ packed in bytes + * const FQ_ELEM *in : FQ Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] |= (in[i * 8 + 6] << 1) | (in[i * 8 + 7] >> 8); + out[i * 9 + 8] |= (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] = (in[i * 8] << 7); + } else if (n_remainder == 2) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] = (in[i * 8 + 1] << 6); + } else if (n_remainder == 3) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] = (in[i * 8 + 2] << 5); + } else if (n_remainder == 4) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] = (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] = (in[i * 8 + 4] << 3); + } else if (n_remainder == 6) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] = (in[i * 8 + 5] << 2); + } else if (n_remainder == 7) { + out[i * 9] = (in[i * 8] >> 1); + out[i * 9 + 1] |= (in[i * 8] << 7) | (in[i * 8 + 1] >> 2); + out[i * 9 + 2] |= (in[i * 8 + 1] << 6) | (in[i * 8 + 2] >> 3); + out[i * 9 + 3] |= (in[i * 8 + 2] << 5) | (in[i * 8 + 3] >> 4); + out[i * 9 + 4] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 5); + out[i * 9 + 5] |= (in[i * 8 + 4] << 3) | (in[i * 8 + 5] >> 6); + out[i * 9 + 6] |= (in[i * 8 + 5] << 2) | (in[i * 8 + 6] >> 7); + out[i * 9 + 7] = (in[i * 8 + 6] << 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fz() + * + * uint8_t *out : Zz packed in bytes + * const FZ_ELEM *in : Zz Vec input, to be packed + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function handles the packing of an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, const size_t outlen, const size_t inlen) { + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 8; i++) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7) | (in[i * 8 + 7]); + } + const uint8_t n_remainder = inlen % 8; + if (n_remainder == 1) { + out[i * 7] = (in[i * 8] << 1); + } else if (n_remainder == 2) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] = (in[i * 8 + 1] << 2); + } else if (n_remainder == 3) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] = (in[i * 8 + 2] << 3); + } else if (n_remainder == 4) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4); + } else if (n_remainder == 5) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] = (in[i * 8 + 4] << 5); + } else if (n_remainder == 6) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] = (in[i * 8 + 5] << 6); + } else if (n_remainder == 7) { + out[i * 7] |= (in[i * 8] << 1) | (in[i * 8 + 1] >> 6); + out[i * 7 + 1] |= (in[i * 8 + 1] << 2) | (in[i * 8 + 2] >> 5); + out[i * 7 + 2] |= (in[i * 8 + 2] << 3) | (in[i * 8 + 3] >> 4); + out[i * 7 + 3] |= (in[i * 8 + 3] << 4) | (in[i * 8 + 4] >> 3); + out[i * 7 + 4] |= (in[i * 8 + 4] << 5) | (in[i * 8 + 5] >> 2); + out[i * 7 + 5] |= (in[i * 8 + 5] << 6) | (in[i * 8 + 6] >> 1); + out[i * 7 + 6] |= (in[i * 8 + 6] << 7); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_vec() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fq(out, in, N, DENSELY_PACKED_FQ_VEC_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_syn() + * + * FQ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fq(out, in, N - K, DENSELY_PACKED_FQ_SYN_SIZE); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_vec() + * + * FZ_ELEM out[N] : FQ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FQ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fz(out, in, N); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_rsdp_g_vec() + * + * FZ_ELEM out[M] : FZ Vec output + * const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE] : FZ Byte input, to be unpckd + * + * This function handles the unpacking of FQ + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]) { + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fz(out, in, M); +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fq() + * + * FQ_ELEM *out : FQ output, unpacked + * const uint8_t *in : FQ Vec input, packed in bytes + * size_t outlen : Length of out + * size_t in : Length of in + * + * This function unpacks an vector of el. in FQ of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen / 9; i++) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + out[i * 8 + 7] = ((in[i * 9 + 7] << 8) & 0x1FF); + out[i * 8 + 7] |= (in[i * 9 + 8]); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + } + if (n_remainder == 2) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + } + if (n_remainder == 3) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + } + if (n_remainder == 4) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + } + if (n_remainder == 5) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + } + if (n_remainder == 6) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + } + if (n_remainder == 7) { + out[i * 8] = (in[i * 9] << 1); + out[i * 8] |= (in[i * 9 + 1] >> 7); + out[i * 8 + 1] = ((in[i * 9 + 1] << 2) & 0x1FF); + out[i * 8 + 1] |= (in[i * 9 + 2] >> 6); + out[i * 8 + 2] = ((in[i * 9 + 2] << 3) & 0x1FF); + out[i * 8 + 2] |= (in[i * 9 + 3] >> 5); + out[i * 8 + 3] = ((in[i * 9 + 3] << 4) & 0x1FF); + out[i * 8 + 3] |= (in[i * 9 + 4] >> 4); + out[i * 8 + 4] = ((in[i * 9 + 4] << 5) & 0x1FF); + out[i * 8 + 4] |= (in[i * 9 + 5] >> 3); + out[i * 8 + 5] = ((in[i * 9 + 5] << 6) & 0x1FF); + out[i * 8 + 5] |= (in[i * 9 + 6] >> 2); + out[i * 8 + 6] = ((in[i * 9 + 6] << 7) & 0x1FF); + out[i * 8 + 6] |= (in[i * 9 + 7] >> 1); + } + +} + +/* + * PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fz() + * + * FZ_ELEM *out : Zz output, unpacked + * const uint8_t *in : Zz Vec input, packed in bytes + * size_t outlen : Length of out + * + * This function unpacks an vector of el. in Zz of arbit. length + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen) { + + size_t i; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen / 8; i++) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + out[i * 8 + 7] = (in[i * 7 + 6] & 0x7F); + } + const uint8_t n_remainder = outlen % 8; + if (n_remainder == 1) { + out[i * 8] = (in[i * 7] >> 1); + } else if (n_remainder == 2) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + } else if (n_remainder == 3) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + } else if (n_remainder == 4) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + } else if (n_remainder == 5) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + } else if (n_remainder == 6) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + } else if (n_remainder == 7) { + out[i * 8] = (in[i * 7] >> 1); + out[i * 8 + 1] = ((in[i * 7] << 6) & 0x7F); + out[i * 8 + 1] |= (in[i * 7 + 1] >> 2); + out[i * 8 + 2] = ((in[i * 7 + 1] << 5) & 0x7F); + out[i * 8 + 2] |= (in[i * 7 + 2] >> 3); + out[i * 8 + 3] = ((in[i * 7 + 2] << 4) & 0x7F); + out[i * 8 + 3] |= (in[i * 7 + 3] >> 4); + out[i * 8 + 4] = ((in[i * 7 + 3] << 3) & 0x7F); + out[i * 8 + 4] |= (in[i * 7 + 4] >> 5); + out[i * 8 + 5] = ((in[i * 7 + 4] << 2) & 0x7F); + out[i * 8 + 5] |= (in[i * 7 + 5] >> 6); + out[i * 8 + 6] = ((in[i * 7 + 5] << 1) & 0x7F); + out[i * 8 + 6] |= (in[i * 7 + 6] >> 7); + } +} + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + size_t in_i = 0; + uint8_t left, right; + uint8_t skip = 0; + if (btr <= 8) { + left = 8 - btr; + right = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + while (right < 8 && in_i < inlen) { + out[i] |= (in[in_i] << (left)) >> right; + right += 8 - left; + left = 8 - btr; + in_i++; + } + if (right != 8) { + in_i--; + left = 2 * 8 - right; + } else { + left = 8 - btr; + } + right = 0; + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = 0; + right = btr - 8; + skip = 0; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < outlen; i++) { + skip = 0; + while (skip == 0 && in_i < inlen) { + out[i] |= (uint8_t)((in[in_i] << left) >> right); + // First case: left == 0, + // i.e.: the current value covers the LSBs of the packed value + if (left == 0) { + skip = 1; + //Case 0: This value was complete, + //i.e. a new one needs to be started + if (right == 0) { + in_i++; + left = 0; + right = btr - 8; + } + //Case 1: These were not the LSBs of this value, + //i.e. its LSBs need to be stored in the next block + else if (right <= 8) { + left = 8 - right; + right = 0; + } else { + right -= 8; + } + + } + // Second case: right == 0 and left > 0, + // i.e.: the current value is complete its LSBs + // are stored in the upper bits + else if (right == 0 && left > 0) { + + right = btr - left; + left = 0; + in_i++; + + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr) { + size_t i; + uint8_t skip = 0; + uint8_t right, left; + size_t out_i; + if (btr <= 8) { + out_i = 0; + right = 8 - btr; + left = 0; + uint8_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + out[out_i] |= (((in[i] >> (right)) << left) & mask); + //Case 1: right > 0, i.e. the current word is done + //and there is a new one waiting in the same input + if (right > 0) { + out_i++; + if (right >= btr) { + right -= btr; + } else { + left = btr - right; + right = 0; + } + } + //Case 2: right == 0 and left > BITS_TO_REPRESENT(Q-1), + //i.e. the current word continues in the next input + else if (right == 0) { + skip = 1; + right = 8 - left; + left = 0; + if (right == 0) { + right = 8 - btr; + } + } + } + } + } else if (btr == 8) { + for (i = 0; i < outlen; i++) { + out[i] = in[i]; + } + } else if ((btr > 8) && (btr <= 16)) { + left = btr - 8; + right = 0; + out_i = 0; + skip = 0; + uint16_t mask = (1 << (btr)) - 1; + for (i = 0; i < outlen; i++) { + out[i] = 0; + } + for (i = 0; i < inlen; i++) { + skip = 0; + while (skip == 0 && out_i < outlen) { + // Shift some value of packed poly to correct position + // and OR it to the target value + out[out_i] |= ((((uint16_t)in[i] << left) >> right) & mask); + // First case: the value was incomplete, + // i.e. the LSBs are in the next block + // This means that left > 0 and right == 0. + if (left > 0 && right == 0) { + // Right shift of next element for remaining bits + if (left <= 8) { + right = 8 - left; + left = 0; + } else { + right = 0; + left -= 8; + } + skip = 1; + } + // Second case: the value was complete, i.e. the LSBs are now stored + else if (left == 0) { + if (right == 0) { + skip = 1; + left = btr - 8; + } else { + left = btr - right; + } + right = 0; + out_i++; + } + if (left == (btr)) { + left = 1; + } + } + } + } + /* PQClean-edit: unused parameter */ + (void)skip; +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.h new file mode 100644 index 000000000..1e7f13fc8 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/pack_unpack.h @@ -0,0 +1,76 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * @author Jonas Schupp + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include +#include + +#include "parameters.h" + +/* compact Z_z/F_q vector encoding functions */ + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_vec(uint8_t out[DENSELY_PACKED_FQ_VEC_SIZE], + const FQ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fq_syn(uint8_t out[DENSELY_PACKED_FQ_SYN_SIZE], + const FQ_ELEM in[N - K]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_vec(uint8_t out[DENSELY_PACKED_FZ_VEC_SIZE], + const FZ_ELEM in[N]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pack_fz_rsdp_g_vec(uint8_t out[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE], + const FZ_ELEM in[M]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fq(uint8_t *out, const FQ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_pack_fz(uint8_t *out, const FZ_ELEM *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_vec(FQ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FQ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fq_syn(FQ_ELEM out[N - K], + const uint8_t in[DENSELY_PACKED_FQ_SYN_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_vec(FZ_ELEM out[N], + const uint8_t in[DENSELY_PACKED_FZ_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_unpack_fz_rsdp_g_vec(FZ_ELEM out[M], + const uint8_t in[DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE]); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fq(FQ_ELEM *out, const uint8_t *in, + size_t outlen, size_t inlen); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_unpack_fz(FZ_ELEM *out, const uint8_t *in, + size_t outlen); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_uint16_t_unpack(uint16_t *out, const uint8_t *in, + size_t outlen, size_t inlen, uint8_t btr); + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generic_uint16_t_pack(uint8_t *out, const uint16_t *in, + size_t outlen, size_t inlen, uint8_t btr); diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/parameters.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/parameters.h new file mode 100644 index 000000000..d3a8a695e --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/parameters.h @@ -0,0 +1,128 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once +#include + +#include "set.h" + +/******************************************************************************/ +/*************************** Base Fields Parameters ***************************/ +/******************************************************************************/ + +/* The same base field and restriction are employed for all categories of RSDP */ +#define Q (509) +#define Z (127) +/* Restricted subgroup generator */ +#define RESTR_G_GEN 16 +#define FZ_ELEM uint8_t +#define FZ_DOUBLEPREC uint16_t +#define FQ_ELEM uint16_t +#define FQ_DOUBLEPREC uint32_t +#define FQ_TRIPLEPREC uint32_t + +/******************************************************************************/ +/****************************** RSDP Parameters *******************************/ +/******************************************************************************/ +/********************************* Category 1 *********************************/ +#define SEC_MARGIN_LAMBDA (256) +#define N (106) +#define K ( 69) +#define M ( 48) + +#define T (996) +#define W (945) +#define POSITION_IN_FW_STRING_T uint16_t + +#define HASH_CSPRNG_DOMAIN_SEP_CONST ((uint16_t)32768) + +/************* Helper macros for derived parameter computation ***************/ + +#define ROUND_UP(amount, round_amt) ( (((amount)+(round_amt)-1)/(round_amt))*(round_amt) ) + +#define IS_REPRESENTABLE_IN_D_BITS(D, N) \ +(((uint32_t) (N)>=(1UL << ((D)-1)) && (uint32_t) (N)<(1UL << (D))) ? (D) : -1) + +#define BITS_TO_REPRESENT(N) \ + ((N) == 0 ? 1 : (15 \ + + IS_REPRESENTABLE_IN_D_BITS( 1, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 2, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 3, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 4, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 5, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 6, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 7, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 8, N) \ + + IS_REPRESENTABLE_IN_D_BITS( 9, N) \ + + IS_REPRESENTABLE_IN_D_BITS(10, N) \ + + IS_REPRESENTABLE_IN_D_BITS(11, N) \ + + IS_REPRESENTABLE_IN_D_BITS(12, N) \ + + IS_REPRESENTABLE_IN_D_BITS(13, N) \ + + IS_REPRESENTABLE_IN_D_BITS(14, N) \ + + IS_REPRESENTABLE_IN_D_BITS(15, N) \ + + IS_REPRESENTABLE_IN_D_BITS(16, N) \ + ) \ + ) + +#define LOG2(L) ( (BITS_TO_REPRESENT(L) > BITS_TO_REPRESENT((L)-1)) ? (BITS_TO_REPRESENT((L)-1)) : (BITS_TO_REPRESENT(L)) ) + +/***************** Derived parameters *****************************************/ +#define SEED_LENGTH_BYTES (SEC_MARGIN_LAMBDA/8) +#define KEYPAIR_SEED_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) +#define HASH_DIGEST_LENGTH (2*(SEC_MARGIN_LAMBDA/8)) +#define SALT_LENGTH_BYTES (2*(SEC_MARGIN_LAMBDA/8)) + +#define NUM_LEAVES_MERKLE_TREE (T) +#define NUM_NODES_MERKLE_TREE (2*NUM_LEAVES_MERKLE_TREE-1) + +/*to be derived via script for each T/W*/ +#define NUM_LEAVES_SEED_TREE ( T ) +// #define NUM_NODES_SEED_TREE ( 2*NUM_LEAVES_SEED_TREE-1 ) +#define NUM_INNER_NODES_SEED_TREE ( NUM_NODES_SEED_TREE-NUM_LEAVES_SEED_TREE ) + +/* Sizes of bitpacked field element vectors + * Bitpacking an n-elements vector of num_bits_for_q-1 bits long values + * will pack 8 values in num_bits_for_q-1 bytes exactly, leaving the remaining + * N % 8 as a tail */ +#define DENSELY_PACKED_FQ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FQ_SYN_SIZE (((N-K)/8)*BITS_TO_REPRESENT(Q-1) + \ + ROUND_UP( (((N-K)%8)*BITS_TO_REPRESENT(Q-1)),8)/8) +#define DENSELY_PACKED_FZ_VEC_SIZE ((N/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((N%8)*BITS_TO_REPRESENT(Z-1)),8)/8) +#define DENSELY_PACKED_FZ_RSDP_G_VEC_SIZE ((M/8)*BITS_TO_REPRESENT(Z-1) + \ + ROUND_UP( ((M%8)*BITS_TO_REPRESENT(Z-1)),8)/8) + +/* Derived parameters computed via compute_derived_parameters.py */ +#define TREE_NODES_TO_STORE 219 +#define NUM_NODES_SEED_TREE 1994 +#define NODES_PER_LEVEL_ARRAY {1, 2, 4, 8, 16, 32, 63, 125, 249, 498, 996} +#define MISSING_NODES_BEFORE_LEVEL_ARRAY {0, 0, 0, 0, 0, 0, 0, 1, 4, 11, 25} +#define BITS_N_ZQ_CT_RNG 1007 +#define BITS_BETA_ZQSTAR_CT_RNG 9070 +#define BITS_V_CT_RNG 23112 +#define BITS_W_CT_RNG 19646 +#define BITS_M_ZZ_CT_RNG 385 +#define BITS_CWSTR_RNG 9947 diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/restr_arith.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/restr_arith.h new file mode 100644 index 000000000..d34f72731 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/restr_arith.h @@ -0,0 +1,121 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "parameters.h" + +#define FZRED_SINGLE(x) (((x) & 0x7f) + ((x) >> 7)) +#define FZRED_DOUBLE(x) FZRED_SINGLE(FZRED_SINGLE(x)) +#define FZRED_OPPOSITE(x) ((x) ^ 0x7f) +#define FZ_DOUBLE_ZERO_NORM(x) (((x) + (((x) + 1) >> 7)) & 0x7f) + +static inline +void fz_dz_norm_sigma(FZ_ELEM v[N]) { + for (int i = 0; i < N; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} + +/* Elements of the restricted subgroups are represented as the exponents of + * the generator */ +static inline +void restr_vec_sub(FZ_ELEM res[N], + const FZ_ELEM a[N], + const FZ_ELEM b[N]) { + for (int i = 0; i < N; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +/* Given the choice of q and z, all elements of G are represented as n-elements + * vectors having powers-of-two in {1,2,...,64}. + * Employ a round of K&R-HW. + * Vectorized comb based HW is also employable, testing a word against + * 0x01...01 + * note that 0x80 is not excluded as sigmas are in double-zero redundant repr. + */ +static inline +int is_fq_vec_in_restr_group(const FQ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + FQ_ELEM tmp; + tmp = ( in[i] - (FQ_ELEM)1 ) & in[i]; + is_in_ok = is_in_ok && (tmp == 0); + } + return is_in_ok; +} + +static inline +int is_zz_vec_in_restr_group(const FZ_ELEM in[N]) { + int is_in_ok = 1; + for (int i = 0; i < N; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} + +/* computes the information word * M_G product to obtain an element of G + * only non systematic portion of M_G = [W I] is used, transposed to improve + * cache friendliness */ +static +void fz_inf_w_by_fz_matrix(FZ_ELEM res[N], + const FZ_ELEM e[M], + FZ_ELEM W_mat[M][N - M]) { + + memset(res, 0, (N - M)*sizeof(FZ_ELEM)); + memcpy(res + (N - M), e, M * sizeof(FZ_ELEM)); + for (int i = 0; i < M; i++) { + for (int j = 0; j < N - M; j++) { + res[j] = FZRED_DOUBLE( (FZ_DOUBLEPREC) res[j] + + (FZ_DOUBLEPREC) e[i] * + (FZ_DOUBLEPREC) W_mat[i][j]); + } + } +} + +static inline +void restr_inf_w_sub(FZ_ELEM res[M], + const FZ_ELEM a[M], + const FZ_ELEM b[M]) { + for (int i = 0; i < M; i++) { + res[i] = FZRED_SINGLE( a[i] + FZRED_OPPOSITE(b[i]) ); + } +} + +static inline +int is_zz_inf_w_valid(const FZ_ELEM in[M]) { + int is_in_ok = 1; + for (int i = 0; i < M; i++) { + is_in_ok = is_in_ok && (in[i] < Z); + } + return is_in_ok; +} +static inline +void fz_dz_norm_delta(FZ_ELEM v[M]) { + for (int i = 0; i < M; i++) { + v[i] = FZ_DOUBLE_ZERO_NORM(v[i]); + } +} diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.c new file mode 100644 index 000000000..9689b7c0b --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.c @@ -0,0 +1,273 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "seedtree.h" +#include +#include // memcpy(...), memset(...) + +#define LEFT_CHILD(i) (2*(i)+1) +#define RIGHT_CHILD(i) (2*(i)+2) +#define PARENT(i) (((i)-1)/2) + +/* Seed tree implementation. The binary seed tree is linearized into an array + * from root to leaves, and from left to right. The nodes are numbered picking + * the indexes from the corresponding full tree, having 2**LOG2(T) leaves */ +#define DIV_BY_TWO_CEIL(i) ((i)/2 + (i) % 2) + +#define TO_PUBLISH 1 +#define NOT_TO_PUBLISH 0 + +/* PQClean-edit: avoid VLA */ +#define CSPRNG_INPUT_LEN (SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + SIZEOF_UINT16) +//const uint32_t csprng_input_len = SALT_LENGTH_BYTES + SEED_LENGTH_BYTES + sizeof(uint16_t); + +/*****************************************************************************/ +/** + * const unsigned char *indices: input parameter denoting an array + * with a number of binary cells equal to "leaves" representing + * the labels of the nodes identified as leaves of the tree[...] + * passed as second parameter. + * A label = 1 means that the byteseed of the node having the same index + * has to be released; = 0, otherwise. + * + * unsigned char *tree: input/output parameter denoting an array + * with a number of binary cells equal to "2*leaves-1"; + * the first "leaves" cells (i.e., the ones with positions from 0 to leaves-1) + * are the ones that will be modified by the current subroutine, + * the last "leaves" cells will be a copy of the input array passed as first + * parameter. + * + * uint64_t leaves: input parameter; + * + */ + +#define NUM_LEAVES_STENCIL_SEED_TREE ( 1UL << LOG2(T) ) +#define NUM_INNER_NODES_STENCIL_SEED_TREE ( NUM_LEAVES_STENCIL_SEED_TREE-1 ) +#define NUM_NODES_STENCIL_SEED_TREE ( 2*NUM_LEAVES_STENCIL_SEED_TREE-1 ) + +static void compute_seeds_to_publish( + /* linearized binary tree of boolean nodes containing + * flags for each node 1-filled nodes are not to be + * released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE], + /* Boolean Array indicating which of the T seeds must be + * released convention as per the above defines */ + const unsigned char indices_to_publish[T]) { + /* the indices to publish may be less than the full leaves, copy them + * into the linearized tree leaves */ + memcpy(flags_tree_to_publish + NUM_INNER_NODES_STENCIL_SEED_TREE, + indices_to_publish, + T); + memset(flags_tree_to_publish, + NOT_TO_PUBLISH, + NUM_INNER_NODES_STENCIL_SEED_TREE * sizeof(unsigned char)); + /* compute the value for the internal nodes of the tree starting from the + * fathers of the leaves, right to left */ + for (int i = NUM_LEAVES_STENCIL_SEED_TREE - 2; i >= 0; i--) { + if ( ( flags_tree_to_publish[LEFT_CHILD(i)] == TO_PUBLISH) && + ( flags_tree_to_publish[RIGHT_CHILD(i)] == TO_PUBLISH) ) { + flags_tree_to_publish[i] = TO_PUBLISH; + } + } +} /* end compute_seeds_to_publish */ + +/** + * unsigned char *seed_tree: + * it is intended as an output parameter; + * storing the linearized binary seed tree + * + * The root seed is taken as a parameter. + * The seed of its TWO children are computed expanding (i.e., shake128...) the + * entropy in "salt" + "seedBytes of the parent" + + * "int, encoded over 16 bits - uint16_t, associated to each node + * from roots to leaves layer-by-layer from left to right, + * counting from 0 (the integer bound with the root node)" + */ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* input buffer to the CSPRNG, contains a salt, the seed to be expanded + * and the integer index of the node being expanded for domain separation */ + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + /* Set the root seed in the tree from the received parameter */ + memcpy(seed_tree, root_seed, SEED_LENGTH_BYTES); + + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i (the root is level 0). This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + /* Generate the log_2(t) layers from the root, each iteration generates a tree + * level; iterate on nodes of the parent level */ + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + int ancestors = 0; + for (int level = 0; level < LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* prepare the CSPRNG input to expand the children of node i */ + memcpy(csprng_input, + seed_tree + father_node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1] ) *SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + if ((RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1]) < NUM_NODES_SEED_TREE ) { + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + } + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + + } + ancestors += (1L << level); + } +} /* end generate_seed_tree */ + +/*****************************************************************************/ + +/*****************************************************************************/ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + // OUTPUT: sequence of seeds to be released + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // INPUT: binary array storing in each cell a binary value (i.e., 0 or 1), + // which in turn denotes if the seed of the node with the same index + // must be released (i.e., cell == 0) or not (i.e., cell == 1). + // Indeed the seed will be stored in the sequence computed as a result into the out[...] array. + // INPUT: binary array denoting which node has to be released (cell == TO_PUBLISH) or not + const unsigned char indices_to_publish[T] + ) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + + int num_seeds_published = 0; + int node_idx = 1; + /* no sense in trying to publish the root node, start examining from level 1 + * */ + + int ancestors = 1; + for (int level = 1; level < LOG2(T) + 1; level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + node_idx = ancestors + node_in_level; + int node_storage_idx = node_idx - missing_nodes_before[level]; + if ( (flags_tree_to_publish[node_idx] == TO_PUBLISH) && + (flags_tree_to_publish[PARENT(node_idx)] == NOT_TO_PUBLISH) ) { + memcpy(seed_storage + num_seeds_published * SEED_LENGTH_BYTES, + seed_tree + node_storage_idx * SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + num_seeds_published++; + } + } + ancestors += (1L << level); + } + + return num_seeds_published; +} /* end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_publish_seeds */ + +/*****************************************************************************/ + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]) { + /* complete linearized binary tree containing boolean values determining + * if a node is to be released or not. Nodes set to 1 are not to be released + * oldest ancestor of sets of nodes equal to 0 are to be released */ + unsigned char flags_tree_to_publish[NUM_NODES_STENCIL_SEED_TREE] = {0}; + compute_seeds_to_publish(flags_tree_to_publish, indices_to_publish); + + const uint32_t csprng_input_len = SALT_LENGTH_BYTES + + SEED_LENGTH_BYTES + + sizeof(uint16_t); + unsigned char csprng_input[CSPRNG_INPUT_LEN]; + CSPRNG_STATE_T tree_csprng_state; + + memcpy(csprng_input + SEED_LENGTH_BYTES, salt, SALT_LENGTH_BYTES); + + int nodes_used = 0; + /* missing_nodes_before[i] contains the total number of missing nodes before + * level i. Level 0 is taken to be the tree root This constant vector is precomputed */ + const int missing_nodes_before[LOG2(T) + 1] = MISSING_NODES_BEFORE_LEVEL_ARRAY; + + /* regenerating the seed tree never starts from the root, as it is never + * disclosed */ + int ancestors = 0; + const int nodes_in_level[LOG2(T) + 1] = NODES_PER_LEVEL_ARRAY; + for (int level = 0; level <= LOG2(T); level++) { + for (int node_in_level = 0; node_in_level < nodes_in_level[level]; node_in_level++ ) { + uint16_t father_node_idx = ancestors + node_in_level; + uint16_t father_node_storage_idx = father_node_idx - missing_nodes_before[level]; + /* if the current node is a seed which was published, memcpy it in place */ + if ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) { + if ( flags_tree_to_publish[PARENT(father_node_idx)] == NOT_TO_PUBLISH ) { + memcpy(seed_tree + SEED_LENGTH_BYTES * (father_node_storage_idx), + stored_seeds + SEED_LENGTH_BYTES * nodes_used, + SEED_LENGTH_BYTES ); + nodes_used++; + } + } + /* if the current node is published and not a leaf, + * CSPRNG-expand its children */ + if ( ( flags_tree_to_publish[father_node_idx] == TO_PUBLISH ) && + ( level < LOG2(T)) ) { + /* prepare the CSPRNG input to expand the children of node father_node_idx */ + memcpy(csprng_input, + seed_tree + (father_node_storage_idx)*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES); + *((uint16_t *)(csprng_input + SALT_LENGTH_BYTES + SEED_LENGTH_BYTES)) = father_node_idx; + /* expand the children (stored contiguously) */ + initialize_csprng(&tree_csprng_state, csprng_input, csprng_input_len); + csprng_randombytes(seed_tree + (LEFT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + csprng_randombytes(seed_tree + (RIGHT_CHILD(father_node_idx) - missing_nodes_before[level + 1])*SEED_LENGTH_BYTES, + SEED_LENGTH_BYTES, + &tree_csprng_state); + + /* PQClean-edit: CSPRNG release context */ + csprng_release(&tree_csprng_state); + } + } + ancestors += (1L << level); + } + return nodes_used; +} /* end regenerate_leaves */ diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.h new file mode 100644 index 000000000..d8f9b35dd --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/seedtree.h @@ -0,0 +1,54 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "csprng_hash.h" +#include "parameters.h" + +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_pseed(unsigned char seed[SEED_LENGTH_BYTES]); +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_ptree(unsigned char seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES]); + +/******************************************************************************/ +void PQCLEAN_CROSSRSDPG256SMALL_CLEAN_generate_seed_tree_from_root(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char root_seed[SEED_LENGTH_BYTES], + const unsigned char salt[SALT_LENGTH_BYTES]) ; + +/******************************************************************************/ +/* returns the number of seeds which have been published */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_publish_seeds(unsigned char *seed_storage, + const unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + // binary array denoting if node has to be released (cell == 0) or not + const unsigned char indices_to_publish[T]); + +/******************************************************************************/ +/* returns the number of seeds which have been used to regenerate the tree */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_regenerate_round_seeds(unsigned char + seed_tree[NUM_NODES_SEED_TREE * SEED_LENGTH_BYTES], + const unsigned char indices_to_publish[T], + const unsigned char *stored_seeds, + const unsigned char salt[SALT_LENGTH_BYTES]); // input diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/set.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/set.h new file mode 100644 index 000000000..8bbf553a0 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/set.h @@ -0,0 +1,23 @@ + +#define RSDPG 1 +#define CATEGORY_5 1 +#define SIG_SIZE 1 + +#undef NO_TREES + +/* liboqs-edit: when compiling avx2 just assume that Intel Instrinsics are available */ +#define IMPLEMENTATION_clean + +/* PQClean-edit: avoid VLA (don't call sizeof() when creating arrays) */ +#define SIZEOF_UINT16 2 + +/* Undefine unused macros to facilitate dead code removal using unifdef */ +#undef SHA_3_LIBKECCAK +/* Variant */ +#undef RSDP +/* Category */ +#undef CATEGORY_1 +#undef CATEGORY_3 +/* Target */ +#undef BALANCED +#undef SPEED diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sha3.h b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sha3.h new file mode 100644 index 000000000..15e445cbe --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sha3.h @@ -0,0 +1,146 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#pragma once + +#include "fips202.h" +/* standalone FIPS-202 implementation has + * different states for SHAKE depending on security level*/ +#define SHAKE_STATE_STRUCT shake256incctx +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x1 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static inline +void xof_shake_init(SHAKE_STATE_STRUCT *state, int val) { + /* PQClean-edit: unused parameter */ + (void)val; + shake256_inc_init(state); +} + +static inline +void xof_shake_update(SHAKE_STATE_STRUCT *state, + const unsigned char *input, + uint32_t inputByteLen) { + shake256_inc_absorb(state, + (const uint8_t *)input, + inputByteLen); +} + +static inline +void xof_shake_final(SHAKE_STATE_STRUCT *state) { + shake256_inc_finalize(state); +} + +static inline +void xof_shake_extract(SHAKE_STATE_STRUCT *state, + unsigned char *output, + uint32_t outputByteLen) { + shake256_inc_squeeze(output, outputByteLen, state); +} + +/* PQClean-edit: CSPRNG release context */ +static inline +void xof_shake_release(SHAKE_STATE_STRUCT *state) { + shake256_inc_ctx_release(state); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x4 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +#include "fips202x4.h" +#define SHAKE_X4_STATE_STRUCT shake256x4incctx +#define SHAKE_X4_INIT shake256x4_inc_init +#define SHAKE_X4_ABSORB shake256x4_inc_absorb +#define SHAKE_X4_FINALIZE shake256x4_inc_finalize +#define SHAKE_X4_SQUEEZE shake256x4_inc_squeeze +#define SHAKE_X4_RELEASE shake256x4_inc_ctx_release + +static inline void xof_shake_x4_init(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_INIT(states); +} +static inline void xof_shake_x4_update(SHAKE_X4_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + const unsigned char *in3, + const unsigned char *in4, + uint32_t singleInputByteLen) { + SHAKE_X4_ABSORB(states, in1, in2, in3, in4, singleInputByteLen); +} +static inline void xof_shake_x4_final(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_FINALIZE(states); +} +static inline void xof_shake_x4_extract(SHAKE_X4_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + unsigned char *out3, + unsigned char *out4, + uint32_t singleOutputByteLen) { + SHAKE_X4_SQUEEZE(out1, out2, out3, out4, singleOutputByteLen, states); +} +static inline void xof_shake_x4_release(SHAKE_X4_STATE_STRUCT *states) { + SHAKE_X4_RELEASE(states); +} + +// %%%%%%%%%%%%%%%%%% Self-contained SHAKE x2 Wrappers %%%%%%%%%%%%%%%%%%%%%%%%%%%% + +/* SHAKE_x2 just calls SHAKE_x1 twice. If a suitable SHAKE_x2 implementation becomes available, it should be used instead */ + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_STATE_STRUCT state2; +} shake_x2_ctx; +#define SHAKE_X2_STATE_STRUCT shake_x2_ctx +static inline void xof_shake_x2_init(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_init(&(states->state1), 0); + xof_shake_init(&(states->state2), 0); +} +static inline void xof_shake_x2_update(SHAKE_X2_STATE_STRUCT *states, + const unsigned char *in1, + const unsigned char *in2, + uint32_t singleInputByteLen) { + xof_shake_update(&(states->state1), (const uint8_t *)in1, singleInputByteLen); + xof_shake_update(&(states->state2), (const uint8_t *)in2, singleInputByteLen); +} +static inline void xof_shake_x2_final(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_final(&(states->state1)); + xof_shake_final(&(states->state2)); +} +static inline void xof_shake_x2_extract(SHAKE_X2_STATE_STRUCT *states, + unsigned char *out1, + unsigned char *out2, + uint32_t singleOutputByteLen) { + xof_shake_extract(&(states->state1), out1, singleOutputByteLen); + xof_shake_extract(&(states->state2), out2, singleOutputByteLen); +} +static inline void xof_shake_x2_release(SHAKE_X2_STATE_STRUCT *states) { + xof_shake_release(&(states->state1)); + xof_shake_release(&(states->state2)); +} + +// %%%%%%%%%%%%%%%%%%%% Parallel SHAKE State Struct %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +typedef struct { + SHAKE_STATE_STRUCT state1; + SHAKE_X2_STATE_STRUCT state2; + SHAKE_X4_STATE_STRUCT state4; +} par_shake_ctx; diff --git a/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sign.c b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sign.c new file mode 100644 index 000000000..e0002d189 --- /dev/null +++ b/src/sig/cross/upcross_cross-rsdpg-256-small_clean/sign.c @@ -0,0 +1,122 @@ +/** + * + * Reference ISO-C11 Implementation of CROSS. + * + * @version 1.1 (March 2023) + * + * @author Alessandro Barenghi + * @author Gerardo Pelosi + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **/ + +#include "api.h" +#include "CROSS.h" +#include "parameters.h" +#include // size_t +#include // memcpy +/*----------------------------------------------------------------------------*/ + +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair(unsigned char *pk, + unsigned char *sk) { + /* keygen cannot fail */ + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_keygen((prikey_t *) sk, + (pubkey_t *) pk); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_keypair + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signed message sm[0],sm[1],...,sm[*smlen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign(unsigned char *sm, + size_t *smlen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk) { // in parameter + /* sign cannot fail */ + memcpy(sm, m, mlen); + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) (sm + mlen)); // out parameter + *smlen = mlen + (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signed message sm[0],sm[1],...,sm[smlen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_open(unsigned char *m, + size_t *mlen, // out parameter + const unsigned char *sm, size_t smlen, // in parameter + const unsigned char *pk) { // in parameter + + /* verify returns 1 if signature is ok, 0 otherwise */ + *mlen = smlen - (size_t) sizeof(CROSS_sig_t); + + memcpy((unsigned char *) m, (const unsigned char *) sm, (size_t) *mlen); + int ok = PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_verify((const pubkey_t *const) + pk, // in parameter + (const char *const) m, (const size_t) * mlen, // in parameter + (const CROSS_sig_t *const) (sm + *mlen)); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_open + +/*----------------------------------------------------------------------------*/ +/* */ +/*... generating a signature sig[0],sig[1],...,sig[*siglen-1] */ +/*... from original message m[0],m[1],...,m[mlen-1] */ +/*... under secret key sk[0],sk[1],... */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature(unsigned char *sig, size_t *siglen, // out parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *sk // in parameter + ) { + /* sign cannot fail */ + PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_sign((const prikey_t *) sk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (CROSS_sig_t *) sig); // out parameter + *siglen = (size_t) sizeof(CROSS_sig_t); + + return 0; // NIST convention: 0 == zero errors +} // end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_signature + +/*----------------------------------------------------------------------------*/ +/* */ +/*. ... verifying a signature sig[0],sig[1],...,sig[siglen-1] */ +/*. ... under public key pk[0],pk[1],... */ +/*. ... and producing original message m[0],m[1],...,m[*mlen-1] */ +int PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify(const unsigned char *sig, size_t siglen, // in parameter + const unsigned char *m, size_t mlen, // in parameter + const unsigned char *pk // in parameter + ) { + + /* PQClean-edit: unused parameter */ + (void)siglen; + + /* verify returns 1 if signature is ok, 0 otherwise */ + int ok = PQCLEAN_CROSSRSDPG256SMALL_CLEAN_CROSS_verify((const pubkey_t *const) pk, // in parameter + (const char *const) m, (const size_t) mlen, // in parameter + (const CROSS_sig_t *const) sig); // in parameter + + return ok - 1; // NIST convention: 0 == zero errors, -1 == error condition +} // end PQCLEAN_CROSSRSDPG256SMALL_CLEAN_crypto_sign_verify + +/*----------------------------------------------------------------------------*/ diff --git a/src/sig/sig.c b/src/sig/sig.c index bab752c60..48a710e86 100644 --- a/src/sig/sig.c +++ b/src/sig/sig.c @@ -43,7 +43,25 @@ OQS_API const char *OQS_SIG_alg_identifier(size_t i) { OQS_SIG_alg_mayo_1, OQS_SIG_alg_mayo_2, OQS_SIG_alg_mayo_3, - OQS_SIG_alg_mayo_5,///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ALG_IDENTIFIER_END + OQS_SIG_alg_mayo_5, + OQS_SIG_alg_cross_rsdp_128_balanced, + OQS_SIG_alg_cross_rsdp_128_fast, + OQS_SIG_alg_cross_rsdp_128_small, + OQS_SIG_alg_cross_rsdp_192_balanced, + OQS_SIG_alg_cross_rsdp_192_fast, + OQS_SIG_alg_cross_rsdp_192_small, + OQS_SIG_alg_cross_rsdp_256_balanced, + OQS_SIG_alg_cross_rsdp_256_fast, + OQS_SIG_alg_cross_rsdp_256_small, + OQS_SIG_alg_cross_rsdpg_128_balanced, + OQS_SIG_alg_cross_rsdpg_128_fast, + OQS_SIG_alg_cross_rsdpg_128_small, + OQS_SIG_alg_cross_rsdpg_192_balanced, + OQS_SIG_alg_cross_rsdpg_192_fast, + OQS_SIG_alg_cross_rsdpg_192_small, + OQS_SIG_alg_cross_rsdpg_256_balanced, + OQS_SIG_alg_cross_rsdpg_256_fast, + OQS_SIG_alg_cross_rsdpg_256_small,///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ALG_IDENTIFIER_END }; if (i >= OQS_SIG_algs_length) { return NULL; @@ -264,6 +282,132 @@ OQS_API int OQS_SIG_alg_is_enabled(const char *method_name) { #else return 0; #endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_small + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_small + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_small + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_small + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_small + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_balanced + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_fast + return 1; +#else + return 0; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_small + return 1; +#else + return 0; +#endif ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ENABLED_CASE_END } else { return 0; @@ -478,6 +622,132 @@ OQS_API OQS_SIG *OQS_SIG_new(const char *method_name) { #else return NULL; #endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_balanced + return OQS_SIG_cross_rsdp_128_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_fast + return OQS_SIG_cross_rsdp_128_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_128_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_128_small + return OQS_SIG_cross_rsdp_128_small_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_balanced + return OQS_SIG_cross_rsdp_192_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_fast + return OQS_SIG_cross_rsdp_192_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_192_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_192_small + return OQS_SIG_cross_rsdp_192_small_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_balanced + return OQS_SIG_cross_rsdp_256_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_fast + return OQS_SIG_cross_rsdp_256_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdp_256_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdp_256_small + return OQS_SIG_cross_rsdp_256_small_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_balanced + return OQS_SIG_cross_rsdpg_128_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_fast + return OQS_SIG_cross_rsdpg_128_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_128_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_128_small + return OQS_SIG_cross_rsdpg_128_small_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_balanced + return OQS_SIG_cross_rsdpg_192_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_fast + return OQS_SIG_cross_rsdpg_192_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_192_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_192_small + return OQS_SIG_cross_rsdpg_192_small_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_balanced)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_balanced + return OQS_SIG_cross_rsdpg_256_balanced_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_fast)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_fast + return OQS_SIG_cross_rsdpg_256_fast_new(); +#else + return NULL; +#endif + + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_cross_rsdpg_256_small)) { +#ifdef OQS_ENABLE_SIG_cross_rsdpg_256_small + return OQS_SIG_cross_rsdpg_256_small_new(); +#else + return NULL; +#endif ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_NEW_CASE_END // EDIT-WHEN-ADDING-SIG } else { diff --git a/src/sig/sig.h b/src/sig/sig.h index bb2a73821..9fd97f27c 100644 --- a/src/sig/sig.h +++ b/src/sig/sig.h @@ -90,12 +90,48 @@ extern "C" { #define OQS_SIG_alg_mayo_3 "MAYO-3" /** Algorithm identifier for MAYO-5 */ #define OQS_SIG_alg_mayo_5 "MAYO-5" +/** Algorithm identifier for cross-rsdp-128-balanced */ +#define OQS_SIG_alg_cross_rsdp_128_balanced "cross-rsdp-128-balanced" +/** Algorithm identifier for cross-rsdp-128-fast */ +#define OQS_SIG_alg_cross_rsdp_128_fast "cross-rsdp-128-fast" +/** Algorithm identifier for cross-rsdp-128-small */ +#define OQS_SIG_alg_cross_rsdp_128_small "cross-rsdp-128-small" +/** Algorithm identifier for cross-rsdp-192-balanced */ +#define OQS_SIG_alg_cross_rsdp_192_balanced "cross-rsdp-192-balanced" +/** Algorithm identifier for cross-rsdp-192-fast */ +#define OQS_SIG_alg_cross_rsdp_192_fast "cross-rsdp-192-fast" +/** Algorithm identifier for cross-rsdp-192-small */ +#define OQS_SIG_alg_cross_rsdp_192_small "cross-rsdp-192-small" +/** Algorithm identifier for cross-rsdp-256-balanced */ +#define OQS_SIG_alg_cross_rsdp_256_balanced "cross-rsdp-256-balanced" +/** Algorithm identifier for cross-rsdp-256-fast */ +#define OQS_SIG_alg_cross_rsdp_256_fast "cross-rsdp-256-fast" +/** Algorithm identifier for cross-rsdp-256-small */ +#define OQS_SIG_alg_cross_rsdp_256_small "cross-rsdp-256-small" +/** Algorithm identifier for cross-rsdpg-128-balanced */ +#define OQS_SIG_alg_cross_rsdpg_128_balanced "cross-rsdpg-128-balanced" +/** Algorithm identifier for cross-rsdpg-128-fast */ +#define OQS_SIG_alg_cross_rsdpg_128_fast "cross-rsdpg-128-fast" +/** Algorithm identifier for cross-rsdpg-128-small */ +#define OQS_SIG_alg_cross_rsdpg_128_small "cross-rsdpg-128-small" +/** Algorithm identifier for cross-rsdpg-192-balanced */ +#define OQS_SIG_alg_cross_rsdpg_192_balanced "cross-rsdpg-192-balanced" +/** Algorithm identifier for cross-rsdpg-192-fast */ +#define OQS_SIG_alg_cross_rsdpg_192_fast "cross-rsdpg-192-fast" +/** Algorithm identifier for cross-rsdpg-192-small */ +#define OQS_SIG_alg_cross_rsdpg_192_small "cross-rsdpg-192-small" +/** Algorithm identifier for cross-rsdpg-256-balanced */ +#define OQS_SIG_alg_cross_rsdpg_256_balanced "cross-rsdpg-256-balanced" +/** Algorithm identifier for cross-rsdpg-256-fast */ +#define OQS_SIG_alg_cross_rsdpg_256_fast "cross-rsdpg-256-fast" +/** Algorithm identifier for cross-rsdpg-256-small */ +#define OQS_SIG_alg_cross_rsdpg_256_small "cross-rsdpg-256-small" ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ALG_IDENTIFIER_END // EDIT-WHEN-ADDING-SIG ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ALGS_LENGTH_START /** Number of algorithm identifiers above. */ -#define OQS_SIG_algs_length 29 +#define OQS_SIG_algs_length 47 ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_ALGS_LENGTH_END /** @@ -277,6 +313,9 @@ OQS_API void OQS_SIG_free(OQS_SIG *sig); #ifdef OQS_ENABLE_SIG_MAYO #include #endif /* OQS_ENABLE_SIG_MAYO */ +#ifdef OQS_ENABLE_SIG_CROSS +#include +#endif /* OQS_ENABLE_SIG_CROSS */ ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_INCLUDE_END // EDIT-WHEN-ADDING-SIG diff --git a/tests/KATs/sig/kats.json b/tests/KATs/sig/kats.json index 918b89365..20bf93e65 100644 --- a/tests/KATs/sig/kats.json +++ b/tests/KATs/sig/kats.json @@ -114,5 +114,77 @@ "SPHINCS+-SHAKE-256s-simple": { "all": "dadcf175289c25aaa530a389cc84154dc4331fabda06ffaf2a292944e4d03841", "single": "37d37c9b43d71341b7dd5da7f8ebbe8bbae3d7bfc53f5378446023cbcf6e04f2" + }, + "cross-rsdp-128-balanced": { + "all": "cafdde1c92c9ce6c2cb67d7c6d225a4bbd103ecaece5cc4ebcb4fe6078e03421", + "single": "370376963a45486d7fc1ec09133ba49b95cad959951aa978ecf4dd82bfaf4f59" + }, + "cross-rsdp-128-fast": { + "all": "543d6ba2072dcd7745989351bbfa06dd9e76eaad0bae094feefb08913c134ba6", + "single": "2f83ba2b950e3690ee84457ac9b4f8e4dcc92ba6c8306f2a096a92cfc44cd05d" + }, + "cross-rsdp-128-small": { + "all": "335007b9fd67563867dd18236d67cd7dd3323c4e9160fa74593e86fe67cee527", + "single": "f5413ecd480f02f6bd4ea149b6303752ed46557d03ca93efb7305e285846ed49" + }, + "cross-rsdp-192-balanced": { + "all": "9579fa618ca2a7306934f550897197004f8f84e6e79eaa66d9fb272ce1b3a8e0", + "single": "bde2ae2d2df0d6a689ff518f851616eeb397516413ce02321c5d0daa311a47d4" + }, + "cross-rsdp-192-fast": { + "all": "1e2ad6016e870b22045bcbb7289e0339173f1d53ac6916320d30bde791ba9366", + "single": "5c22c2da9e54fe975c2ebd59cbe2921c45143e440546d694714351c2e8f46442" + }, + "cross-rsdp-192-small": { + "all": "516770faad19aea2ad991a8ed877edd812608a2c4918d56e91f99ceecdfdc55d", + "single": "da919e5ed4f4a1bd7b460638be3b9df8182f7a5e0dc16f32949ebcdb97e66772" + }, + "cross-rsdp-256-balanced": { + "all": "d884d43507345a27f15323f422a0962679144d335ba6a2e25f9c34b3ee0fd412", + "single": "f76d5c76b285b0aa923284f98c1e6e5bcb8a3119d8e677fbfea2f4b855e9fa27" + }, + "cross-rsdp-256-fast": { + "all": "4fed5777a8250b30674b3dc445e27fec3894eab5b6c3f041659bf19252f7be5c", + "single": "96141b55b061b32300296593cc5caa116ec5a4f6c0a3a1119031e343d6a0f52e" + }, + "cross-rsdp-256-small": { + "all": "fb394a489be1c8b77899ba11c986f982aff32cc409b642f54768f9d6b604d496", + "single": "79d581fcd9797c0f1ad7b0fd6816437e85851ac6d77e4c7f9a07b0b76ce1eb2a" + }, + "cross-rsdpg-128-balanced": { + "all": "abf62c013af1725a431dde2e4916f4794261f5a0b13cac0c42ec4e4fb06f3cec", + "single": "f1ebf700c39e84da903b513fc7b2455d03008b14b3e8c713de450fd96202a50e" + }, + "cross-rsdpg-128-fast": { + "all": "252c376bc12e4312ee8092d237dbe92744a761d99f55e4159b75851199098cac", + "single": "9b5c81d03be6b1d06d862694c5ee08f5fd4ccb2fcdec646cadb7b9d697adf4fb" + }, + "cross-rsdpg-128-small": { + "all": "c5430a08c04016643cde1e27a9eac63eeaca00e8057f3415454ef5de34e05b30", + "single": "1ad0ad8138042035f02a38fa69fb10c991dc047b7a14f8144bf2a65c224c1028" + }, + "cross-rsdpg-192-balanced": { + "all": "c56154d3b52d6f96406605dd1dfe4fcdf681098c4ca2598fe1ed89c0803e6b91", + "single": "c86a888478ba5e273b703afa59f31189349fab764cc26e7a29b4be1b6ec1f938" + }, + "cross-rsdpg-192-fast": { + "all": "14346a0e23dd8457a81ad31dcd34db2913c9bb175126f68a30f310e6461981c0", + "single": "9c51b9856e5102048264de43b9793f6d9126a28f8e258a9bb200fab5f193fc95" + }, + "cross-rsdpg-192-small": { + "all": "10425df46eabe4278f73aa92776e1e99f5c5fba5fc0dec6a9115e15dd6413e6c", + "single": "0d30f7c5bb81251ee466973ad1d2691dfb83beaa8afbdebe9dd580923d3ae5d0" + }, + "cross-rsdpg-256-balanced": { + "all": "b0600c444baf395bf4c9bc478869db951e7c95973df3aa70293a342dea6d3943", + "single": "0f15ab5044b3a232d53069819d1d61bfa13562084777c18b3e72cf261ab86a2f" + }, + "cross-rsdpg-256-fast": { + "all": "bcaf651564297db58cf7b0ae35b9601d3c0900da700894c857ace54abdfb75ef", + "single": "1aa83013bad860b40a4680299ff5c8cd794aad6578e4c4929a5a2d244c9a0f85" + }, + "cross-rsdpg-256-small": { + "all": "bc72c3a05ea4ab7166741442d8c71cda05408190674072f8ce5506f8e7db59fd", + "single": "55ef76e57c7b2b0c7fa6c665b1173761afba86db2bc02852db154b7d2885ed43" } } \ No newline at end of file diff --git a/tests/constant_time/sig/issues.json b/tests/constant_time/sig/issues.json index b5ea3f5a1..aa5102178 100644 --- a/tests/constant_time/sig/issues.json +++ b/tests/constant_time/sig/issues.json @@ -1,5 +1,23 @@ { + "cross-rsdp-128-balanced": [], + "cross-rsdp-128-fast": [], + "cross-rsdp-128-small": [], + "cross-rsdp-192-balanced": [], + "cross-rsdp-192-fast": [], + "cross-rsdp-192-small": [], + "cross-rsdp-256-balanced": [], + "cross-rsdp-256-fast": [], + "cross-rsdp-256-small": [], + "cross-rsdpg-128-balanced": [], + "cross-rsdpg-128-fast": [], + "cross-rsdpg-128-small": [], + "cross-rsdpg-192-balanced": [], + "cross-rsdpg-192-fast": [], + "cross-rsdpg-192-small": [], + "cross-rsdpg-256-balanced": [], + "cross-rsdpg-256-fast": [], + "cross-rsdpg-256-small": [], "Dilithium2": [], "Dilithium3": [], "Dilithium5": [], diff --git a/tests/constant_time/sig/passes.json b/tests/constant_time/sig/passes.json index 06dcd036d..65247af66 100644 --- a/tests/constant_time/sig/passes.json +++ b/tests/constant_time/sig/passes.json @@ -1,5 +1,23 @@ { + "cross-rsdp-128-balanced": ["cross"], + "cross-rsdp-128-fast": ["cross"], + "cross-rsdp-128-small": ["cross"], + "cross-rsdp-192-balanced": ["cross"], + "cross-rsdp-192-fast": ["cross"], + "cross-rsdp-192-small": ["cross"], + "cross-rsdp-256-balanced": ["cross"], + "cross-rsdp-256-fast": ["cross"], + "cross-rsdp-256-small": ["cross"], + "cross-rsdpg-128-balanced": ["cross"], + "cross-rsdpg-128-fast": ["cross"], + "cross-rsdpg-128-small": ["cross"], + "cross-rsdpg-192-balanced": ["cross"], + "cross-rsdpg-192-fast": ["cross"], + "cross-rsdpg-192-small": ["cross"], + "cross-rsdpg-256-balanced": ["cross"], + "cross-rsdpg-256-fast": ["cross"], + "cross-rsdpg-256-small": ["cross"], "Dilithium2": ["dilithium", "dilithium-avx2", "dilithium-aarch64"], "Dilithium3": ["dilithium", "dilithium-avx2", "dilithium-aarch64"], "Dilithium5": ["dilithium", "dilithium-avx2", "dilithium-aarch64"], diff --git a/tests/constant_time/sig/passes/cross b/tests/constant_time/sig/passes/cross new file mode 100644 index 000000000..622299155 --- /dev/null +++ b/tests/constant_time/sig/passes/cross @@ -0,0 +1,110 @@ +# Keygen + +{ + Rejection sampling for matrix "V" + Memcheck:Cond + fun:CSPRNG_fq_mat + fun:expand_public_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_keygen +} + +{ + Rejection sampling for matrix "W" (RSDPG variant) + Memcheck:Cond + fun:CSPRNG_fz_mat + fun:expand_public_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_keygen +} + +{ + Rejection sampling for vector "eta" + Memcheck:Cond + fun:CSPRNG_zz_vec + fun:PQCLEAN_CROSSRSDP*_CROSS_keygen +} + +{ + Rejection sampling for vector "zeta" (RSDPG variant) + Memcheck:Cond + fun:CSPRNG_zz_inf_w + fun:PQCLEAN_CROSSRSDP*_CROSS_keygen +} + +# Sign + +{ + Rejection sampling for matrix "V" + Memcheck:Cond + fun:CSPRNG_fq_mat + fun:expand_public_seed + fun:expand_private_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for matrix "W" (RSDPG variant) + Memcheck:Cond + fun:CSPRNG_fz_mat + fun:expand_public_seed + fun:expand_private_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + + +{ + Rejection sampling for vector "eta" + Memcheck:Cond + fun:CSPRNG_zz_vec + fun:expand_private_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for vector "zeta" (RSDPG variant) + Memcheck:Cond + fun:CSPRNG_zz_inf_w + fun:expand_private_seed + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for vector "eta_tilde" + Memcheck:Cond + fun:CSPRNG_zz_vec + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for vector "zeta_tilde" (RSDPG variant) + Memcheck:Cond + fun:CSPRNG_zz_inf_w + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for vector "u_tilde" + Memcheck:Cond + fun:CSPRNG_fq_vec + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for vector "beta" + Memcheck:Cond + fun:CSPRNG_fq_vec_beta + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for Fisher-Yates shuffle of challenge vector "b" + Memcheck:Cond + fun:PQCLEAN_CROSSRSDP*_expand_digest_to_fixed_weight + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} + +{ + Rejection sampling for Fisher-Yates shuffle of challenge vector "b" + Memcheck:Value8 + fun:PQCLEAN_CROSSRSDP*_expand_digest_to_fixed_weight + fun:PQCLEAN_CROSSRSDP*_CROSS_sign +} diff --git a/tests/kat_sig.c b/tests/kat_sig.c index ffb045692..0c873afc9 100644 --- a/tests/kat_sig.c +++ b/tests/kat_sig.c @@ -312,6 +312,186 @@ OQS_STATUS combine_message_signature(uint8_t **signed_msg, size_t *signed_msg_le memcpy(*signed_msg, signature, signature_len); memcpy(*signed_msg + signature_len, msg, msg_len); return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-128-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-128-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-128-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-192-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-192-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-192-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-256-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-256-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdp-256-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-128-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-128-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-128-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-192-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-192-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-192-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-256-balanced")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-256-fast")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; + } else if (0 == strcmp(sig->method_name, "cross-rsdpg-256-small")) { + // signed_msg = msg || signature + *signed_msg_len = msg_len + signature_len; + *signed_msg = malloc(*signed_msg_len); + if (*signed_msg == NULL) { + return OQS_ERROR; + } + memcpy(*signed_msg, msg, msg_len); + memcpy(*signed_msg + msg_len, signature, signature_len); + return OQS_SUCCESS; ///// OQS_COPY_FROM_UPSTREAM_FRAGMENT_COMBINE_MESSAGE_SIGNATURE_END } else { return OQS_ERROR; diff --git a/tests/test_sig.c b/tests/test_sig.c index 185ef169c..e94d3034c 100644 --- a/tests/test_sig.c +++ b/tests/test_sig.c @@ -224,8 +224,8 @@ int main(int argc, char **argv) { OQS_STATUS rc; #if OQS_USE_PTHREADS #define MAX_LEN_SIG_NAME_ 64 - // don't run MAYO_5 in threads because of large stack usage - char no_thread_sig_patterns[][MAX_LEN_SIG_NAME_] = {"MAYO-5"}; + // don't run algorithms with large stack usage in threads + char no_thread_sig_patterns[][MAX_LEN_SIG_NAME_] = {"MAYO-5", "cross-rsdp-128-small", "cross-rsdp-192-small", "cross-rsdp-256-balanced", "cross-rsdp-256-small", "cross-rsdpg-192-small", "cross-rsdpg-256-small"}; int test_in_thread = 1; for (size_t i = 0 ; i < sizeof(no_thread_sig_patterns) / MAX_LEN_SIG_NAME_; ++i) { if (strstr(alg_name, no_thread_sig_patterns[i]) != NULL) { diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 29be2f9d5..3d7bed0e7 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -128,6 +128,19 @@ if(CONFIG_LIBOQS) set(OQS_ENABLE_SIG_MAYO OFF) endif() + if(CONFIG_LIBOQS_ENABLE_SIG_CROSS) + set(OQS_ENABLE_SIG_CROSS ON) + # Disable CROSS variants with large stack usage + set(OQS_ENABLE_SIG_cross_rsdp_128_small OFF) + set(OQS_ENABLE_SIG_cross_rsdp_192_small OFF) + set(OQS_ENABLE_SIG_cross_rsdp_256_balanced OFF) + set(OQS_ENABLE_SIG_cross_rsdp_256_small OFF) + set(OQS_ENABLE_SIG_cross_rsdpg_192_small OFF) + set(OQS_ENABLE_SIG_cross_rsdpg_256_small OFF) + else() + set(OQS_ENABLE_SIG_CROSS OFF) + endif() + # Add the actual liboqs targets add_subdirectory(.. build) diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 9f3481701..9ad402f4d 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -71,4 +71,9 @@ config LIBOQS_ENABLE_SIG_MAYO default y depends on LIBOQS +config LIBOQS_ENABLE_SIG_CROSS + bool "Enable the CROSS signature algorithm" + default y + depends on LIBOQS + endmenu