├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindGMP.cmake ├── FindNTL.cmake └── Findsodium.cmake ├── include ├── algebra.hpp ├── bulletproof.hpp ├── constraints.hpp ├── libmerlin │ └── merlin.h ├── merlin.hpp ├── pedersen.hpp ├── point25519.hpp ├── regevEnc.hpp ├── regevProofs.hpp ├── scalar25519.hpp ├── shamir.hpp ├── ternaryMatrix.hpp ├── tests.hpp └── utils.hpp ├── src ├── 25519 │ └── curve25519.cpp ├── algebra │ ├── foursquares.cpp │ └── ternaryMatrix.cpp ├── dlproofs │ ├── bulletproof.cpp │ ├── constraints.cpp │ └── naive.cpp ├── libmerlin │ └── merlin.c ├── main.cpp ├── regev │ ├── regevEnc.cpp │ ├── regevProofs-utils.cpp │ └── regevProofs.cpp └── tools │ └── shamir.cpp └── tests ├── CMakeLists.txt ├── test_algebra.cpp ├── test_bulletproof.cpp ├── test_constraint.cpp ├── test_foursquares.cpp ├── test_merlin.cpp ├── test_pedersen.cpp ├── test_point25519.cpp ├── test_regevEnc.cpp ├── test_regevProofs.cpp ├── test_scalar25519.cpp ├── test_shamir.cpp └── test_ternaryMatrix.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(lwe-pvss VERSION 0.1.0) 3 | 4 | # Use -std=c++17 as default. 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | # Dependencies are GMP/NTL and libsodium 8 | 9 | # Add directories for extra cmake files, e.g. to find NTK and other libraries 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 11 | 12 | find_package(GMP) 13 | # define the following variables (see ./cmake/FindNTL.make) 14 | # GMP_FOUND 15 | # GMP_LIBRARIES 16 | # GMP_HEADERS 17 | 18 | find_package(NTL) 19 | # define the following variables (see ./cmake/FindNTL.make) 20 | # NTL_FOUND 21 | # NTL_LIBRARIES 22 | # NTL_HEADERS 23 | 24 | find_package(sodium) 25 | # define the following variables (see ./cmake/FindSodium.make) 26 | # sodium_FOUND 27 | # sodium_INCLUDE_DIR 28 | # sodium_LIBRARY_DEBUG 29 | # sodium_LIBRARY_RELEASE 30 | 31 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") 32 | 33 | # Source files are found under the src directory 34 | file(GLOB SOURCES src/*/*.cpp src/libmerlin/merlin.c) 35 | add_library(lwe-pvss STATIC ${SOURCES}) 36 | target_include_directories(lwe-pvss PUBLIC 37 | ${NTL_INCLUDE_PATHS} 38 | ${sodium_INCLUDE_DIR} 39 | ) 40 | 41 | set(PROJECT_LIBS lwe-pvss ${sodium_LIBRARY_DEBUG} ${NTL_LIBRARIES} ${GMP_LIBRARIES} pthread) 42 | # SET(EXTRAOBJS "${CMAKE_CURRENT_SOURCE_DIR}/src/libmerlin/merlin.o") 43 | 44 | add_executable(lwe-pvss-main src/main.cpp) 45 | target_link_libraries(lwe-pvss-main PUBLIC ${PROJECT_LIBS}) 46 | 47 | include(CTest) 48 | enable_testing() 49 | add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests") 50 | 51 | set(CPACK_PROJECT_NAME ${PROJECT_NAME}) 52 | set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) 53 | include(CPack) 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 LWE-PVSS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp-lwevss 2 | C++ implementation of VSS using LWE encryption and proofs, as described in the article: 3 | 4 | [Practical Non-interactive Publicly Verifiable Secret Sharing with Thousands of Parties](https://eprint.iacr.org/2021/1397), by Craig Gentry and Shai Halevi and Vadim Lyubashevsky, [Cryptology ePrint Archive](https://eprint.iacr.org): Report 2021/1397. 5 | 6 | This implementation was written mostly by [Shai Halevi](https://alum.mit.edu/www/shaih). 7 | It is a proof-of-concept and **is not suitable for use in production**. 8 | The code is documented internally, and we made some effort to separate out performance-sensitive parts so they can be optimized on their own. 9 | 10 | ### Structure 11 | 12 | This is a cmake project, but the directory structure tries to mimic Go conventions to some extent. Different subdirectories of `src` correspond roughly to what the Go packages would be. Current subdirectories are: 13 | 14 | + `25519` - wrapper around libsodium, namespace CRV25519. Provide classes `Point` and `Scalar`. See `point25519.hpp`, `scalar25519.hpp` in the include directory. 15 | 16 | + `algebra` - wrapper around NTL, namespace ALGEBRA. Should make using packages other than NTL in the future a little easier. See `algebra.hpp` in the include directory. 17 | 18 | + `dlproofs` - implementation of [Bulletproofs](https://crypto.stanford.edu/bulletproofs/)-like proofs, namespace DLPROOFS. Provides proofs for linear and quadratic constraints, as well as proving the norm-squared (mod P) of a vector. See `constraints.hpp` `pedersen.hpp` `bulletproof.hpp` in the includes directory. 19 | 20 | * `tools` - currently contains only tools related to Shamir secret sharing, namespace TOOLS. See `shamir.hpp` in the includes directory. 21 | 22 | + `regev` - implementation of Regev encryption and proofs related to it, namespace REGEVENC. See `regevEnc.hpp` and `regevProofs.hpp` in the includes directory. 23 | 24 | + `libmerlin` - Henry de Valence's one-file "C" implementation of Merlin transcripts. Taken (with minor fixes) from https://github.com/hdevalence/libmerlin (commit 4bf6228), but separated the "c" and "h" files to different directories. A C++ wrapper found in `merlin.hpp` in the include directory. 25 | 26 | ### Dependencies 27 | 28 | * [libsodium](https://github.com/jedisct1/libsodium) for implementation of arithmetic over Curve25519 29 | 30 | * [NTL](https://libntl.org/) and [GMP](https://gmplib.org/) underlying operations for Regev encryption 31 | 32 | ### License 33 | 34 | This implementation is provided under the MIT license 35 | 36 | -------------------------------------------------------------------------------- /cmake/FindGMP.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021, LWE-PVSS 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject 9 | # to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included 12 | # in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | # OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | # 23 | # January 2021, adapted from https://github.com/homenc/HElib/blob/master/cmake/FindGMP.cmake 24 | 25 | 26 | # Use cmake standard find_library package 27 | include(FindPackageHandleStandardArgs) 28 | 29 | # Standard `lib` and system specific `lib32/64` directory. 30 | list(APPEND lib_suffixes "lib" "${CMAKE_INSTALL_LIBDIR}") 31 | 32 | # Standard `include` system specific directory (usually the same). 33 | list(APPEND header_suffixes "include" "${CMAKE_INSTALL_INCLUDEDIR}") 34 | 35 | # If CMAKE_LIBRARY_ARCHITECTURE is defined (e.g.: `x86_64-linux-gnu`) add that 36 | # after `lib` and `lib32/64` and after `include` suffixes(required for Ubuntu) 37 | if (CMAKE_LIBRARY_ARCHITECTURE) 38 | list(APPEND lib_suffixes 39 | "lib/${CMAKE_LIBRARY_ARCHITECTURE}" 40 | "${CMAKE_INSTALL_LIBDIR}/${CMAKE_LIBRARY_ARCHITECTURE}") 41 | list(APPEND header_suffixes 42 | "include/${CMAKE_LIBRARY_ARCHITECTURE}" 43 | "${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_LIBRARY_ARCHITECTURE}") 44 | endif (CMAKE_LIBRARY_ARCHITECTURE) 45 | 46 | find_library(GMP_LIBRARIES 47 | NAMES gmp libgmp 48 | PATH_SUFFIXES ${lib_suffixes} 49 | DOC "GMP library") 50 | 51 | find_path(GMP_HEADERS 52 | NAMES gmp.h 53 | PATH_SUFFIXES ${header_suffixes} 54 | DOC "GMP headers") 55 | 56 | unset(lib_suffixes) 57 | unset(header_suffixes) 58 | 59 | # Check the library has been found, handle QUIET/REQUIRED arguments and set 60 | # GMP_FOUND accordingly or raise the error 61 | find_package_handle_standard_args(GMP 62 | REQUIRED_VARS GMP_LIBRARIES GMP_HEADERS) 63 | 64 | set(GMP_INCLUDE_PATHS "${GMP_HEADERS}") 65 | 66 | # Mark GMP_LIBRARIES GMP_INCLUDE_PATHS as advanced variables 67 | mark_as_advanced(GMP_LIBRARIES GMP_INCLUDE_PATHS) 68 | -------------------------------------------------------------------------------- /cmake/FindNTL.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021, LWE-PVSS 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject 9 | # to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included 12 | # in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | # OTHER DEALINGS IN THE SOFTWARE. 21 | # 22 | # January 2021, adapted from https://github.com/homenc/HElib/blob/master/cmake/FindNTL.cmake 23 | 24 | # Use cmake standard find_library package 25 | include(FindPackageHandleStandardArgs) 26 | 27 | # Standard `lib` and system specific `lib32/64` directory. 28 | list(APPEND lib_suffixes "lib" "${CMAKE_INSTALL_LIBDIR}") 29 | 30 | # Standard `include` system specific directory (usually the same). 31 | list(APPEND header_suffixes "include/NTL" "${CMAKE_INSTALL_INCLUDEDIR}/NTL") 32 | 33 | # If CMAKE_LIBRARY_ARCHITECTURE is defined (e.g.: `x86_64-linux-gnu`) add that 34 | # after `lib` and `lib32/64` and after `include` suffixes(required for Ubuntu) 35 | if (CMAKE_LIBRARY_ARCHITECTURE) 36 | list(APPEND lib_suffixes 37 | "lib/${CMAKE_LIBRARY_ARCHITECTURE}" 38 | "${CMAKE_INSTALL_LIBDIR}/${CMAKE_LIBRARY_ARCHITECTURE}") 39 | list(APPEND header_suffixes 40 | "include/${CMAKE_LIBRARY_ARCHITECTURE}/NTL" 41 | "${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_LIBRARY_ARCHITECTURE}/NTL") 42 | endif (CMAKE_LIBRARY_ARCHITECTURE) 43 | 44 | find_library(NTL_LIBRARIES 45 | NAMES ntl libntl 46 | PATH_SUFFIXES "${lib_suffixes}" 47 | DOC "NTL library") 48 | 49 | find_path(NTL_HEADERS 50 | NAMES config.h 51 | PATH_SUFFIXES ${header_suffixes} 52 | DOC "NTL headers") 53 | 54 | 55 | unset(lib_suffixes) 56 | unset(header_suffixes) 57 | 58 | # Check the library has been found, handle QUIET/REQUIRED arguments and set 59 | # NTL_FOUND accordingly or raise the error 60 | find_package_handle_standard_args(NTL 61 | REQUIRED_VARS NTL_LIBRARIES NTL_HEADERS) 62 | 63 | set(NTL_INCLUDE_PATHS "${NTL_HEADERS}/..") 64 | 65 | # Mark NTL_LIBRARIES NTL_INCLUDE_PATHS as advanced variables 66 | mark_as_advanced(NTL_LIBRARIES NTL_INCLUDE_PATHS) 67 | -------------------------------------------------------------------------------- /cmake/Findsodium.cmake: -------------------------------------------------------------------------------- 1 | # Written in 2016 by Henrik Steffen Gaßmann 2 | # 3 | # To the extent possible under law, the author(s) have dedicated all 4 | # copyright and related and neighboring rights to this software to the 5 | # public domain worldwide. This software is distributed without any warranty. 6 | # 7 | # You should have received a copy of the CC0 Public Domain Dedication 8 | # along with this software. If not, see 9 | # 10 | # http://creativecommons.org/publicdomain/zero/1.0/ 11 | # 12 | ######################################################################## 13 | # Tries to find the local libsodium installation. 14 | # 15 | # On Windows the sodium_DIR environment variable is used as a default 16 | # hint which can be overridden by setting the corresponding cmake variable. 17 | # 18 | # Once done the following variables will be defined: 19 | # 20 | # sodium_FOUND 21 | # sodium_INCLUDE_DIR 22 | # sodium_LIBRARY_DEBUG 23 | # sodium_LIBRARY_RELEASE 24 | # 25 | # 26 | # Furthermore an imported "sodium" target is created. 27 | # 28 | 29 | if (CMAKE_C_COMPILER_ID STREQUAL "GNU" 30 | OR CMAKE_C_COMPILER_ID STREQUAL "Clang") 31 | set(_GCC_COMPATIBLE 1) 32 | endif() 33 | 34 | # static library option 35 | if (NOT DEFINED sodium_USE_STATIC_LIBS) 36 | option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF) 37 | endif() 38 | if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST)) 39 | unset(sodium_LIBRARY CACHE) 40 | unset(sodium_LIBRARY_DEBUG CACHE) 41 | unset(sodium_LIBRARY_RELEASE CACHE) 42 | unset(sodium_DLL_DEBUG CACHE) 43 | unset(sodium_DLL_RELEASE CACHE) 44 | set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable") 45 | endif() 46 | 47 | 48 | ######################################################################## 49 | # UNIX 50 | if (UNIX) 51 | # import pkg-config 52 | find_package(PkgConfig QUIET) 53 | if (PKG_CONFIG_FOUND) 54 | pkg_check_modules(sodium_PKG QUIET libsodium) 55 | endif() 56 | 57 | if(sodium_USE_STATIC_LIBS) 58 | foreach(_libname ${sodium_PKG_STATIC_LIBRARIES}) 59 | if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a 60 | list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a") 61 | endif() 62 | endforeach() 63 | list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES) 64 | 65 | # if pkgconfig for libsodium doesn't provide 66 | # static lib info, then override PKG_STATIC here.. 67 | if (NOT sodium_PKG_STATIC_FOUND) 68 | set(sodium_PKG_STATIC_LIBRARIES libsodium.a) 69 | endif() 70 | 71 | set(XPREFIX sodium_PKG_STATIC) 72 | else() 73 | if (NOT sodium_PKG_FOUND) 74 | set(sodium_PKG_LIBRARIES sodium) 75 | endif() 76 | 77 | set(XPREFIX sodium_PKG) 78 | endif() 79 | 80 | find_path(sodium_INCLUDE_DIR sodium.h 81 | HINTS ${${XPREFIX}_INCLUDE_DIRS} 82 | ) 83 | find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES} 84 | HINTS ${${XPREFIX}_LIBRARY_DIRS} 85 | ) 86 | find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES} 87 | HINTS ${${XPREFIX}_LIBRARY_DIRS} 88 | ) 89 | 90 | 91 | ######################################################################## 92 | # Windows 93 | elseif (WIN32) 94 | set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory") 95 | mark_as_advanced(sodium_DIR) 96 | 97 | find_path(sodium_INCLUDE_DIR sodium.h 98 | HINTS ${sodium_DIR} 99 | PATH_SUFFIXES include 100 | ) 101 | 102 | if (MSVC) 103 | # detect target architecture 104 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[ 105 | #if defined _M_IX86 106 | #error ARCH_VALUE x86_32 107 | #elif defined _M_X64 108 | #error ARCH_VALUE x86_64 109 | #endif 110 | #error ARCH_VALUE unknown 111 | ]=]) 112 | try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" 113 | OUTPUT_VARIABLE _COMPILATION_LOG 114 | ) 115 | string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}") 116 | 117 | # construct library path 118 | if (_TARGET_ARCH STREQUAL "x86_32") 119 | string(APPEND _PLATFORM_PATH "Win32") 120 | elseif(_TARGET_ARCH STREQUAL "x86_64") 121 | string(APPEND _PLATFORM_PATH "x64") 122 | else() 123 | message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.") 124 | endif() 125 | string(APPEND _PLATFORM_PATH "/$$CONFIG$$") 126 | 127 | if (MSVC_VERSION LESS 1900) 128 | math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60") 129 | else() 130 | math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50") 131 | endif() 132 | string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}") 133 | 134 | if (sodium_USE_STATIC_LIBS) 135 | string(APPEND _PLATFORM_PATH "/static") 136 | else() 137 | string(APPEND _PLATFORM_PATH "/dynamic") 138 | endif() 139 | 140 | string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}") 141 | string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}") 142 | 143 | find_library(sodium_LIBRARY_DEBUG libsodium.lib 144 | HINTS ${sodium_DIR} 145 | PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} 146 | ) 147 | find_library(sodium_LIBRARY_RELEASE libsodium.lib 148 | HINTS ${sodium_DIR} 149 | PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} 150 | ) 151 | if (NOT sodium_USE_STATIC_LIBS) 152 | set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES}) 153 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") 154 | find_library(sodium_DLL_DEBUG libsodium 155 | HINTS ${sodium_DIR} 156 | PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} 157 | ) 158 | find_library(sodium_DLL_RELEASE libsodium 159 | HINTS ${sodium_DIR} 160 | PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} 161 | ) 162 | set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK}) 163 | endif() 164 | 165 | elseif(_GCC_COMPATIBLE) 166 | if (sodium_USE_STATIC_LIBS) 167 | find_library(sodium_LIBRARY_DEBUG libsodium.a 168 | HINTS ${sodium_DIR} 169 | PATH_SUFFIXES lib 170 | ) 171 | find_library(sodium_LIBRARY_RELEASE libsodium.a 172 | HINTS ${sodium_DIR} 173 | PATH_SUFFIXES lib 174 | ) 175 | else() 176 | find_library(sodium_LIBRARY_DEBUG libsodium.dll.a 177 | HINTS ${sodium_DIR} 178 | PATH_SUFFIXES lib 179 | ) 180 | find_library(sodium_LIBRARY_RELEASE libsodium.dll.a 181 | HINTS ${sodium_DIR} 182 | PATH_SUFFIXES lib 183 | ) 184 | 185 | file(GLOB _DLL 186 | LIST_DIRECTORIES false 187 | RELATIVE "${sodium_DIR}/bin" 188 | "${sodium_DIR}/bin/libsodium*.dll" 189 | ) 190 | find_library(sodium_DLL_DEBUG ${_DLL} libsodium 191 | HINTS ${sodium_DIR} 192 | PATH_SUFFIXES bin 193 | ) 194 | find_library(sodium_DLL_RELEASE ${_DLL} libsodium 195 | HINTS ${sodium_DIR} 196 | PATH_SUFFIXES bin 197 | ) 198 | endif() 199 | else() 200 | message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") 201 | endif() 202 | 203 | 204 | ######################################################################## 205 | # unsupported 206 | else() 207 | message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") 208 | endif() 209 | 210 | 211 | ######################################################################## 212 | # common stuff 213 | 214 | # extract sodium version 215 | if (sodium_INCLUDE_DIR) 216 | set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h") 217 | if (EXISTS _VERSION_HEADER) 218 | file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT) 219 | string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1" 220 | sodium_VERSION "${_VERSION_HEADER_CONTENT}") 221 | set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE) 222 | endif() 223 | endif() 224 | 225 | # communicate results 226 | include(FindPackageHandleStandardArgs) 227 | find_package_handle_standard_args( 228 | sodium # The name must be either uppercase or match the filename case. 229 | REQUIRED_VARS 230 | sodium_LIBRARY_RELEASE 231 | sodium_LIBRARY_DEBUG 232 | sodium_INCLUDE_DIR 233 | VERSION_VAR 234 | sodium_VERSION 235 | ) 236 | 237 | if(Sodium_FOUND) 238 | set(sodium_LIBRARIES 239 | optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG}) 240 | endif() 241 | 242 | # mark file paths as advanced 243 | mark_as_advanced(sodium_INCLUDE_DIR) 244 | mark_as_advanced(sodium_LIBRARY_DEBUG) 245 | mark_as_advanced(sodium_LIBRARY_RELEASE) 246 | if (WIN32) 247 | mark_as_advanced(sodium_DLL_DEBUG) 248 | mark_as_advanced(sodium_DLL_RELEASE) 249 | endif() 250 | 251 | # create imported target 252 | if(sodium_USE_STATIC_LIBS) 253 | set(_LIB_TYPE STATIC) 254 | else() 255 | set(_LIB_TYPE SHARED) 256 | endif() 257 | add_library(sodium ${_LIB_TYPE} IMPORTED) 258 | 259 | set_target_properties(sodium PROPERTIES 260 | INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}" 261 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 262 | ) 263 | 264 | if (sodium_USE_STATIC_LIBS) 265 | set_target_properties(sodium PROPERTIES 266 | INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC" 267 | IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" 268 | IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" 269 | ) 270 | else() 271 | if (UNIX) 272 | set_target_properties(sodium PROPERTIES 273 | IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" 274 | IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" 275 | ) 276 | elseif (WIN32) 277 | set_target_properties(sodium PROPERTIES 278 | IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}" 279 | IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}" 280 | ) 281 | if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND")) 282 | set_target_properties(sodium PROPERTIES 283 | IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}" 284 | ) 285 | endif() 286 | if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND")) 287 | set_target_properties(sodium PROPERTIES 288 | IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}" 289 | IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}" 290 | IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}" 291 | ) 292 | endif() 293 | endif() 294 | endif() 295 | -------------------------------------------------------------------------------- /include/algebra.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _ALGEBRA_HPP_ 2 | #define _ALGEBRA_HPP_ 3 | /* algebra.hpp - an NTL-compatibility thin layer 4 | * Copyright (C) 2021, LWE-PVSS 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject 12 | * to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | * OTHER DEALINGS IN THE SOFTWARE. 24 | **/ 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* This header provides copmatibility with NTL, inside the ALGEBRA namespace. 31 | * Modules that use it should use the names that it provides rather than 32 | * directly use NTL. Specifically it provides the following names: 33 | * - BigInt -> NTL:ZZ 34 | * - Scalar -> NTL::ZZ_p 35 | * - Element -> NTL::ZZ_pE (element in GF(p^e)) 36 | * - BIVector-> NTL::vec_ZZ 37 | * - SVector -> NTL::vec_ZZ_p 38 | * - EVector -> NTL::vec_ZZ_pE 39 | * - SMatrix -> NTL::mat_ZZ_p 40 | * - EMatrix -> NTL::mat_ZZ_pE 41 | * 42 | * The users can assume a function conv(x,y) that converts between these 43 | * types as apropriate. Everything else is provided explicitly in this header. 44 | */ 45 | namespace ALGEBRA { 46 | // in liue of "using X as Y", bring the relevant NTL types to this namespace 47 | typedef NTL::ZZ BigInt; 48 | typedef NTL::ZZ_p Scalar; 49 | typedef NTL::ZZ_pX SPoly; 50 | typedef NTL::ZZ_pE Element; 51 | typedef NTL::vec_ZZ BIVector; 52 | typedef NTL::vec_ZZ_p SVector; 53 | typedef NTL::mat_ZZ_p SMatrix; 54 | typedef NTL::vec_ZZ_pE EVector; 55 | typedef NTL::mat_ZZ_pE EMatrix; 56 | 57 | inline BigInt divCeil(const BigInt& x, const BigInt& overY) { 58 | return (x+overY-1)/overY; 59 | } 60 | inline BigInt divCeil(const BigInt& x, long overY) { 61 | return (x+overY-1)/overY; 62 | } 63 | 64 | inline int bytesPerScalar() { return (NumBits(NTL::ZZ_p::modulus())+7)/8; } 65 | inline int scalarsPerElement() { return NTL::ZZ_pE::degree(); } 66 | 67 | inline const Scalar& zeroScalar() { return NTL::ZZ_p::zero(); } 68 | inline Scalar& randomizeScalar(Scalar& s) { NTL::random(s); return s; } 69 | inline Element& randomizeElement(Element& e) { NTL::random(e); return e; } 70 | inline long randBitsize(size_t n) {return NTL::RandomBits_long(n);} 71 | inline BigInt& randBitsize(BigInt& bi, size_t n) { 72 | NTL::RandomBits(bi, n); 73 | return bi; 74 | } 75 | inline size_t randomBit() {return randBitsize(1);} 76 | 77 | inline BIVector& resize(BIVector& vec, size_t n) {vec.SetLength(n); return vec;} 78 | inline SVector& resize(SVector& vec, size_t n) {vec.SetLength(n); return vec;} 79 | inline EVector& resize(EVector& vec, size_t n) {vec.SetLength(n); return vec;} 80 | inline SMatrix& resize(SMatrix& mat, size_t n, size_t m) { 81 | mat.SetDims(n,m); return mat; 82 | } 83 | inline EMatrix& resize(EMatrix& mat, size_t n, size_t m) { 84 | mat.SetDims(n,m); return mat; 85 | } 86 | 87 | inline BigInt square(const BigInt& x) {return NTL::sqr(x);} 88 | inline BigInt sqrt(const BigInt& x) {return NTL::SqrRoot(x);} 89 | 90 | // returns the smallest ell such that 2^{ell} >= n 91 | inline size_t log2roundUp(const BigInt& n) { 92 | if (NTL::IsZero(n)) return 0; 93 | return NTL::NumBits(n-1); 94 | } 95 | 96 | // some conversions 97 | inline BigInt toBigInt(long n) { 98 | NTL::ZZ num(NTL::INIT_SIZE,4); 99 | conv(num, n); 100 | return num; 101 | } 102 | inline BigInt scalar2bigInt(const Scalar& s) {return NTL::conv(s);} 103 | inline const Scalar& coeff(const Element& e, size_t idx) { 104 | return NTL::coeff(NTL::rep(e), idx); 105 | } 106 | 107 | inline void bigIntBytes(unsigned char *buf, const BigInt& bi, size_t bufSize){ 108 | NTL::BytesFromZZ(buf, bi, bufSize); 109 | } 110 | inline void scalarBytes(unsigned char *buf, const Scalar& s, size_t bufSize){ 111 | bigIntBytes(buf, NTL::rep(s), bufSize); 112 | } 113 | inline void elementBytes(unsigned char *buf, const Element& e, size_t bufSize){ 114 | const SVector& v = NTL::rep(e).rep; // the underlying representation as vec_ZZ_p 115 | size_t len = bytesPerScalar(); // =32 116 | for (size_t i=0; i0; i++) { 117 | if (len > bufSize) 118 | len = bufSize; 119 | NTL::BytesFromZZ(buf, NTL::rep(v[i]), len); // gen the next few bytes 120 | bufSize -= len; 121 | buf += len; 122 | } 123 | if (bufSize > 0) // zero-out the rest of the buffer 124 | memset(buf, 0, bufSize); 125 | } 126 | 127 | inline void scalarFromBytes(Scalar& s, const unsigned char *buf, size_t bufSize){ 128 | NTL::ZZ n(NTL::INIT_SIZE,4); 129 | NTL::ZZFromBytes(n, buf, bufSize); 130 | NTL::conv(s,n); 131 | } 132 | inline void elementFromBytes(Element& e, const unsigned char *buf, size_t bufSize){ 133 | NTL::ZZ_pX px; 134 | size_t len = bytesPerScalar(); 135 | for (size_t i=0; i0; i++) { 136 | if (len > bufSize) 137 | len = bufSize; 138 | NTL::ZZ n(NTL::INIT_SIZE,4); 139 | NTL::ZZFromBytes(n, buf, len); 140 | SetCoeff(px, i, NTL::conv(n)); 141 | bufSize -= len; 142 | buf += len; 143 | } 144 | conv(e,px); 145 | } 146 | 147 | // Other utilities 148 | inline Scalar innerProduct(const SVector& v1, const SVector& v2) { 149 | Scalar s; 150 | NTL::InnerProduct(s,v1,v2); 151 | return s; 152 | } 153 | inline Element innerProduct(const EVector& v1, const SVector& v2) { 154 | Element e; 155 | int len = std::min(v1.length(), v2.length()); 156 | for (int i=0; i 170 | VecType& push_back(VecType &v, const ElemType& s) { 171 | v.append(s); 172 | return v; 173 | } 174 | 175 | // convert between an element and a vector of scalars 176 | inline void conv(Element& e, const SVector& v) { 177 | NTL::ZZ_pX& px = (NTL::ZZ_pX&) rep(e); // dirty: discards const-ness 178 | size_t n = std::min(scalarsPerElement(), (int)v.length()); 179 | for (size_t i=0; i(NTL::log(NTL::to_RR(x))/0.69314718056); 208 | } 209 | 210 | typedef NTL::RandomStreamPush PRGbackupClass; // backup/restore of PRG state 211 | inline void initRandomness(const std::string& st) { 212 | NTL::SetSeed((unsigned char*)st.data(), st.length()); 213 | } 214 | 215 | // Break a vector v over GF(p^ell) (with coefficients in [+-P/2]) into 216 | // two "digit" vectors hi,lo, where lo = (v mod radix) \in [+-radix/2] 217 | // and hi = (v-lo)/radix. 218 | void breakTwoDigits(EVector& hi, EVector& lo, const EVector& v, const BigInt& radix); 219 | 220 | inline BigInt balanced(const Scalar& s) { 221 | static auto Pover2 = NTL::ZZ_p::modulus()/2; 222 | if (rep(s) > Pover2) 223 | return rep(s)-NTL::ZZ_p::modulus(); 224 | else 225 | return rep(s); 226 | } 227 | inline BIVector balanced(const SVector& sv) { 228 | BIVector bv; resize(bv, sv.length()); 229 | for (int i=0; i balanced(const EVector& ev) { 238 | NTL::Vec vbv; 239 | vbv.SetLength(ev.length()); 240 | for (int i=0; iPover2) 250 | mat[i][j] -= NTL::ZZ_p::modulus(); 251 | } 252 | return mat; 253 | } 254 | inline NTL::Mat balanced(const EMatrix& emat) { 255 | NTL::Mat mbv; 256 | mbv.SetDims(emat.NumRows(), emat.NumCols()); 257 | for (int i=0; i NTL::ZZ_p::modulus()/2) 269 | st << (szz - NTL::ZZ_p::modulus()); 270 | else 271 | st << szz; 272 | return st; 273 | } 274 | inline std::ostream& printSvec(std::ostream& st, const SVector& sv) { 275 | st << "["; 276 | for (size_t i=0; i 27 | #include 28 | 29 | #include "scalar25519.hpp" 30 | #include "point25519.hpp" 31 | #include "constraints.hpp" 32 | #include "pedersen.hpp" 33 | #include "merlin.hpp" 34 | 35 | namespace DLPROOFS { 36 | using CRV25519::Scalar, CRV25519::Point, Merlin::MerlinBPctx; 37 | 38 | // Linear proofs: input are constraint cnstr=(sum_{i=1}^n a_{j_i}*X_{j_i}=b), 39 | // and scalars xes = ( x_{j_i} ) that satisfy it, namely 40 | // sum_{i=1}^n a_{j_i} * x_{j_i} = b. 41 | // Returns a NIZK proof of knowledge of scalars that satisfy this constraint. 42 | // 43 | // Specificaly it writes to its output a vector V of elements such that: 44 | // + V[0] = r*base + \sum_i x_{j_i}*G_{j_i} is Pedresen commitment to the x'es 45 | // + from cnstr and V[0] anyone can compute 46 | // - the generator F = hashtoCurve(cnstr,V[0]) 47 | // - a modified commitment C = r*base + b*F + \sum_i x_{j_i}*G_{j_i} 48 | // + The rest of V is a linear Bulletproof, a ZKPOK for (r and) x_{j_i}'s 49 | // that match the commitment C and satisfy the linear constraint. 50 | 51 | // "Flatten" the representation of the statement (+optional witness), 52 | // replacing the maps and structures with just three vectors, one for 53 | // the constraints, one for the generators, and an optional one for 54 | // the witness. 55 | struct FlatLinStmt { 56 | std::vector generators; 57 | std::vector statement; 58 | Scalar equalsTo; 59 | std::vector witness; 60 | 61 | FlatLinStmt() = default; 62 | FlatLinStmt(const PedersenContext& ped, 63 | const LinConstraint& cnstr, const PtxtVec& xes=PtxtVec()); 64 | }; 65 | 66 | // The transcript of a linear bulletproof-like proof 67 | struct LinPfTranscript { 68 | std::string tag; 69 | Point C; // commitment to witness 70 | std::vector Ls,Rs; // holds the L's and R's 71 | Point S; // the last proof element 72 | Scalar a, r; // the last two scalars 73 | 74 | explicit LinPfTranscript(const std::string& t): tag(t) {} 75 | }; 76 | 77 | // The lower-level routines. These functions modify in place the lists 78 | // of points and scalars in the FlatLinStmt while processing the proof 79 | void proveLinear(LinPfTranscript& proof, Scalar r, MerlinBPctx& mer, 80 | Scalar* const as, Scalar* const bs, Point* const gs, size_t n); 81 | bool verifyLinear(LinPfTranscript& proof, Scalar* const bs, Point* const gs, 82 | size_t n, const Scalar&e, MerlinBPctx& mer, Scalar *wOffsets=nullptr); 83 | // If the optional wOffsets is specified, then proof.C is a 84 | // commitment to witness+wOffset rather than to the witness itself. 85 | 86 | inline LinPfTranscript proveLinear(const std::string& tag, 87 | const LinConstraint& cnstr, const PtxtVec& xes) { 88 | LinPfTranscript proof(tag); 89 | MerlinBPctx mer(tag); // Make a Merlin state that includes the statement 90 | PedersenContext ped(tag); 91 | mer.processConstraint("constraint", cnstr); 92 | 93 | FlatLinStmt st(ped, cnstr, xes); // "Faltten" the statement and witness 94 | 95 | // Compute and push {"C": commitment to the xes} 96 | Scalar r = CRV25519::randomScalar(); 97 | proof.C = DLPROOFS::commit(st.generators.data(), st.witness.data(), 98 | st.generators.size(), r); // commitment to the xes=as 99 | 100 | size_t n = st.generators.size(); 101 | Scalar* const as = st.witness.data(); // these are the xes 102 | Scalar* const bs = st.statement.data(); // these are the constraints 103 | Point* const gs = st.generators.data(); // these are the generators 104 | 105 | proveLinear(proof, r, mer, as, bs, gs, n); // The actual proof 106 | return proof; 107 | } 108 | 109 | inline bool verifyLinear(const LinConstraint& cnstr, LinPfTranscript& proof) { 110 | MerlinBPctx mer(proof.tag); // Make a Merlin state that includes the statement 111 | PedersenContext ped(proof.tag); 112 | mer.processConstraint("constraint", cnstr); 113 | 114 | FlatLinStmt st(ped, cnstr); // "Faltten" the statement 115 | size_t n = st.generators.size(); 116 | Scalar* const bs = st.statement.data(); // these are the constraints 117 | Point* const gs = st.generators.data(); // these are the generators 118 | return verifyLinear(proof, bs, gs, n, st.equalsTo, mer); 119 | // The actual verification 120 | } 121 | 122 | // Quadratic proofs: quadratic constraints have the form 123 | // cnstr = (sum_{i=1}^n x_{j_i} * y_{j_i} = b) 124 | // where b is a public scalar and the x_{ij}'s and y_{ij}'s are the 125 | // witnesses. Returns a NIZK proof of knowledge of scalars that satisfy 126 | // this constraint. 127 | // 128 | // Specificaly it writes to its output a vector V of elements such that: 129 | // + V[0] =r1*base +\sum_i x_{j_i}*G_{j_i} +\sum_i y_{j_i}*H_{j_i} is 130 | // Pedresen commitment to the x'es y's 131 | // + from cnstr and V[0] anyone can compute 132 | // - the generator F = hashtoCurve(cnstr,V[0]) 133 | // - commitment C = r*base +b*F +\sum_i x_{j_i}*G_{j_i} +y_{j_i}*H_{j_i} 134 | // + The rest of V is a quadratic Bulletproof, a ZKPOK for r,x_{j_i},y_{j_i} 135 | // that match the commitment C and satisfy the quadratic constraint. 136 | 137 | // "Flatten" the representation of the statement (+optional witness), 138 | // replacing the maps and structures with simple vectors. 139 | struct FlatQuadStmt { 140 | std::vector gs, hs; 141 | Scalar equalsTo; 142 | std::vector wG, wH; 143 | 144 | FlatQuadStmt() = default; 145 | FlatQuadStmt(const PedersenContext& ped, const QuadConstraint& cnstr, 146 | const PtxtVec& xes=PtxtVec(), const PtxtVec& ys=PtxtVec()); 147 | }; 148 | // The transcript of a quadratic bulletproof-like proof 149 | struct QuadPfTranscript { 150 | std::string tag; 151 | Point C ; // commitments to witnesses 152 | std::vector Ls,Rs; // holds the L's and R's 153 | Point S1,S2; // the last proof elements 154 | Scalar a, b, r; // the last three scalars 155 | 156 | explicit QuadPfTranscript(const std::string& t): tag(t) {} 157 | }; 158 | 159 | // The lower-level routines. These functions modify in place the lists 160 | // of points and scalars in the FlatQuadStmt while processing the proof 161 | void proveQuadratic(QuadPfTranscript& pf, Scalar r, MerlinBPctx& m, 162 | Point* gs, Scalar* as, Point* hs, Scalar* bs, size_t n); 163 | bool verifyQuadratic(QuadPfTranscript& pf, Point* const gs, Point* const hs, 164 | size_t n, const Scalar& eq, MerlinBPctx& mer, 165 | Scalar *uOffsets=nullptr, Scalar *vOffsets=nullptr); 166 | // If the optional uOffsets, vOffsets are specified, then proof.C is 167 | // a commitment to u+uOffset, v+vOffset rather than to u,v themselves. 168 | 169 | inline QuadPfTranscript proveQuadratic(const std::string& tag, 170 | const QuadConstraint& cnstr, const PtxtVec& xs, const PtxtVec& ys) { 171 | QuadPfTranscript pf(tag); 172 | MerlinBPctx mer(tag); // Make a Merlin state that includes the statement 173 | PedersenContext ped(tag); 174 | mer.processConstraint("constraint", cnstr); 175 | FlatQuadStmt st(ped, cnstr, xs, ys); // "Faltten" statement and witnesses 176 | 177 | size_t n = st.gs.size(); 178 | Scalar* const as = st.wG.data(); // the a witnesses 179 | Scalar* const bs = st.wH.data(); // the b witnesses 180 | Point* const gs = st.gs.data(); // the G generators 181 | Point* const hs = st.hs.data(); // the H generators 182 | 183 | // Compute and push {"C": commitment to the xes} 184 | Scalar r = CRV25519::randomScalar(); 185 | pf.C = DLPROOFS::commit2(gs, as, hs, bs, n, r); // commitment to the x'es and y's 186 | 187 | proveQuadratic(pf, r, mer, gs, as, hs, bs, n); // The actual proof 188 | return pf; 189 | } 190 | 191 | inline bool verifyQuadratic(const QuadConstraint& cnstr, QuadPfTranscript& pf) { 192 | MerlinBPctx mer(pf.tag); // Make a Merlin state that includes the statement 193 | mer.processConstraint("constraint", cnstr); 194 | PedersenContext ped(pf.tag); 195 | FlatQuadStmt st(ped, cnstr); // "Faltten" the statement 196 | size_t n = st.gs.size(); 197 | Point* const gs = st.gs.data(); // the G generators 198 | Point* const hs = st.hs.data(); // the H generators 199 | return verifyQuadratic(pf, gs, hs, n, st.equalsTo, mer); 200 | // The actual verification 201 | } 202 | 203 | // Norm proofs are similar to quadratic, but for the case of xs=ys. It gets 204 | // the vector, computes its norm, and convert to a quadratic proos using some 205 | // more challenges from the verifier. Returns the norm-squared and a proof. 206 | Scalar proveNormSquared(QuadPfTranscript& pf, MerlinBPctx& mer, PtxtVec& vec); 207 | bool verifyNormSquared(const std::set& indexes, 208 | const Scalar& normS, QuadPfTranscript& pf); 209 | 210 | // I/O 211 | std::ostream& operator<<(std::ostream& os, const LinPfTranscript& tr); 212 | std::istream& operator>>(std::istream& is, LinPfTranscript& tr); 213 | 214 | } // namespace DLPROOFS 215 | #endif // ifndef _BOOLETPROOF_HPP_ 216 | -------------------------------------------------------------------------------- /include/constraints.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _CONSTRAINTS_HPP_ 2 | #define _CONSTRAINTS_HPP_ 3 | /* constraints.hpp - manipulating constraints over Scalars 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // Hopefully there are only two lines in this file that are specific 33 | // to our scalar space being modulo the group order of Curve 25519, 34 | // namely this include and the "using scalar" below. If ever we want to 35 | // extend the context to other types of scalars, then maybe it can be 36 | // done by having an abstract scalar type with specific implementations. 37 | #include "scalar25519.hpp" 38 | 39 | /* We manipulate two type sof constraints: 40 | * 41 | * 1. Linear constraints of the form 42 | * 43 | * sum_{i=1}^n x_{j_i} * a_{j_i} = b (mod P) 44 | * 45 | * where the j_i's are the indexes, the x'es are the variables, and 46 | * the a's and b are the public coefficients. Such constraints are 47 | * represented by the scalar b and a map 48 | * 49 | * [ j_1->a_{j_1}, j_2->a_{j_2}, ..., j_n->a_{j_n} ] 50 | * 51 | * with the (sorted, unique) indexes j_i in the range [1,N]. 52 | * 53 | * 2. Quadratic constraints of the form 54 | * 55 | * sum_{i=1}^n x_{j_i} * y_{j_i} = b (mod P) 56 | * 57 | * where the j_i's are the indexes and the x'es and y's are variables. 58 | * These constraints are represented by the scalar b and by the set 59 | * { j_1,...,j_n } of (sorted, unique) indexes j_i in the range [1,N]. 60 | * 61 | * 62 | * For a linear constraint we will have an associated Pedersen vector 63 | * commitment 64 | * 65 | * C = base*r + \sum_{i=1}^n x_{j_i} * G_{j_i}. 66 | * 67 | * For an inner-product constraint we will have two associated Pedersen 68 | * vector commitments 69 | * 70 | * C1 = base*r1 + \sum_{i=1}^n x_{j_i}*G_{j_i} 71 | * C2 = base*r2 + \sum_{i=1}^n y_{j_i}*H_{j_i} 72 | * 73 | * (where we have the same set of indexes j_i for the x'es and y's. 74 | * These commitments are defined with respect to a public "canonical" 75 | * list of generators G_1,...,G_N, H_1,...,H_N. 76 | * 77 | * The main way to manipulate these constraints is by aggregating them, 78 | * where linear constraints are aggregated by taking a random linear 79 | * combination of them and quadratic constraints on disjoint variables 80 | * are aggregated by concatenating them. 81 | * 82 | * Once we have only one constraint of each type we can "move" all the 83 | * variables that appear in both to the quadratic constraint, resulting 84 | * in constraints that are almost disjoint on their sets of variables. 85 | * This transformation introduces one additional variable that appears 86 | * in both output constraints, hence only "almost disjoint". 87 | * 88 | * Another transformation that we use is taking a quadratic constraint 89 | * CNSTR = [ sum_{i=1}^n x_{j_i}*y_{j_i} = b (mod P) ], 90 | * and producing another constraint with the same indexes but different 91 | * variables and different scalar 92 | * CNSTR' = [ sum_{i=1}^n x'_{j_i}*y'_{j_i} = b' (mod P) ]. 93 | * This transformation has the property that knowledge of x',y' variables 94 | * satisfying CNSTR' implies also knowldge of x,y variables satisfying the 95 | * original CNSTR, which in addition satisfy x_{j_i}=y_{j_i} for all i. 96 | */ 97 | 98 | namespace DLPROOFS { // FIXME: what namespace should we use here? 99 | 100 | extern CRV25519::Scalar dbgSum; 101 | 102 | using CRV25519::Scalar; 103 | typedef std::map PtxtVec; 104 | 105 | class LinConstraint { 106 | public: 107 | PtxtVec terms; // the j -> a terms 108 | Scalar equalsTo; // the b coefficient 109 | 110 | // Add a term idx->s to a constraint. 111 | // If this constraint already has a term idx->s' then s will be added 112 | // to s', resulting in idx->(s+s'). Otherwise idx->s will be inserted 113 | // to the constraint. Returns true if a term idx->s' existed before, 114 | // false if inserted anew. 115 | LinConstraint& addTerm(size_t idx, const Scalar& s); 116 | 117 | bool operator==(const LinConstraint& other) { 118 | if (equalsTo != other.equalsTo) 119 | return false; 120 | return (terms == other.terms); 121 | } 122 | bool operator!=(const LinConstraint& other) { return !(*this==other);} 123 | 124 | // Merges multiple constraints by taking a linear combination of them 125 | // The resulting constraint is \sum_i constraints[i]*coeffs[i], where 126 | // addition semantics is as in the addTerm method above. 127 | void merge(const std::vector& constraints, const std::vector& coeffs); 128 | 129 | //void merge2(const std::vector& constraints, const std::vector& coeffs); 130 | /** used to debug and test the merge method **/ 131 | 132 | void debugPrint() const; 133 | }; 134 | 135 | class QuadConstraint { 136 | public: 137 | std::set indexes; // the j_i indexes 138 | Scalar equalsTo; // the b coefficient 139 | 140 | // Add index to a quadratic constraint (throws if already there) 141 | QuadConstraint& addIdx(size_t idx) { 142 | auto ret = indexes.insert(idx); 143 | if (!ret.second) { // index was already there 144 | throw std::runtime_error("QuadConstraint::addIdx: index "+std::to_string(idx)+" already exists"); 145 | } 146 | return *this; 147 | } 148 | 149 | bool operator==(const QuadConstraint& other) { 150 | return (equalsTo == other.equalsTo && indexes == other.indexes); 151 | } 152 | bool operator!=(const QuadConstraint& other) { return !(*this==other);} 153 | 154 | // Merge two quadratic constraints, throws if they are not disjoint 155 | QuadConstraint& operator+=(const QuadConstraint& other); 156 | 157 | // Merges multiple *disjoint* constraints by taking a linear combination 158 | // of them. The resulting constraint has indexes which is the disjoint 159 | // union, and equaltsTo = \sum_i equalsTo[i]*coeffs[i]^2. 160 | void merge(const std::vector& constraints, const std::vector& coeffs); 161 | 162 | void debugPrint() const; 163 | }; 164 | 165 | inline int largestKey(const LinConstraint& c) { 166 | return (c.terms.empty()? -1 : c.terms.rbegin()->first); 167 | } 168 | inline int largestKey(const QuadConstraint& c) { 169 | return (c.indexes.empty()? -1 : *(c.indexes.rbegin())); 170 | } 171 | 172 | Scalar innerProduct(const PtxtVec& v1, const PtxtVec& v2); 173 | 174 | // Split the src map into the intersection with teh sliptBy set and 175 | // the set-difference between them. Return an index one larger than 176 | // the max index in src and splitBy 177 | size_t splitPtxtVec(PtxtVec& intersection, PtxtVec& setDiff, 178 | const PtxtVec& src, const std::set& splitBy); 179 | 180 | // Remove from the linear constraint all variables that also appear in 181 | // the quadratic constraint. This function intoruces one more variable 182 | // that was not in the original constraints, which will appear in both 183 | // the new constraints. Returns the index of the new variable. 184 | size_t makeAlmostDisjoint(LinConstraint& lin, QuadConstraint& quad, const Scalar& s); 185 | 186 | // Debugging functions 187 | 188 | // Check that the constrains hold and that the indexes match 189 | bool checkConstraint(const LinConstraint& cnstr, const PtxtVec& witness); 190 | bool checkConstraint(const QuadConstraint& cnstr, const PtxtVec& xs, const PtxtVec& ys); 191 | 192 | // Check that the constrains hold but allow the witness to have more 193 | // variables than just what's in the constraint 194 | bool checkConstraintLoose(const LinConstraint& cnstr, const PtxtVec& witness); 195 | bool checkConstraintLoose(const QuadConstraint& cnstr, 196 | const PtxtVec& xs, const PtxtVec& ys); 197 | 198 | } /* end of namespace DLPROOFS */ 199 | #endif // ifndef _CONSTRAINTS_HPP_ 200 | -------------------------------------------------------------------------------- /include/libmerlin/merlin.h: -------------------------------------------------------------------------------- 1 | #ifndef MERLIN_H 2 | #define MERLIN_H 3 | #define __STDC_WANT_LIB_EXT1__ 1 4 | #include 5 | #include 6 | 7 | /* XXX can these be made opaque without malloc? */ 8 | 9 | typedef struct merlin_strobe128_ { 10 | /* XXX endianness */ 11 | union { 12 | uint64_t state[25]; 13 | uint8_t state_bytes[200]; 14 | }; 15 | uint8_t pos; 16 | uint8_t pos_begin; 17 | uint8_t cur_flags; 18 | } merlin_strobe128; 19 | 20 | typedef struct merlin_transcript_ { 21 | merlin_strobe128 sctx; 22 | } merlin_transcript; 23 | 24 | typedef struct merlin_rng_ { 25 | merlin_strobe128 sctx; 26 | uint8_t finalized; 27 | } merlin_rng; 28 | 29 | void merlin_transcript_init(merlin_transcript* mctx, 30 | const uint8_t* label, 31 | size_t label_len); 32 | 33 | void merlin_transcript_commit_bytes(merlin_transcript* mctx, 34 | const uint8_t* label, 35 | size_t label_len, 36 | const uint8_t* data, 37 | size_t data_len); 38 | 39 | void merlin_transcript_challenge_bytes(merlin_transcript* mctx, 40 | const uint8_t* label, 41 | size_t label_len, 42 | uint8_t* buffer, 43 | size_t buffer_len); 44 | 45 | void merlin_rng_init(merlin_rng* mrng, const merlin_transcript* mctx); 46 | 47 | void merlin_rng_commit_witness_bytes(merlin_rng* mrng, 48 | const uint8_t* label, 49 | size_t label_len, 50 | const uint8_t* witness, 51 | size_t witness_len); 52 | 53 | void merlin_rng_finalize(merlin_rng* mrng, const uint8_t entropy[32]); 54 | 55 | void merlin_rng_random_bytes(merlin_rng* mrng, uint8_t* buffer, size_t buffer_len); 56 | 57 | void merlin_rng_wipe(merlin_rng* mrng); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/merlin.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _MERLIN_H_ 2 | #define _MERLIN_H_ 3 | /* merlin.hpp - Wrappers around Merlin context 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include "scalar25519.hpp" 27 | #include "point25519.hpp" 28 | #include "constraints.hpp" 29 | extern "C" 30 | { 31 | #include "libmerlin/merlin.h" // "C" implementation of Melin Transcripts 32 | } 33 | 34 | // We use Merlin transcripts https://merlin.cool to generate the challenges 35 | // and blinding factors for the Fiat-Shamir proofs. The current state of a 36 | // Merlin context includes a hash of all the transcript so far, the next 37 | // challenge is computed as a hash of the current state, and a blinding 38 | // factor is generated by hashing the current state, secrets held by the 39 | // prover, and 32 bytes of fresh randomness. 40 | // 41 | // FIXME: We rely on the "one C file implementation" of Merlin from 42 | // https://github.com/hdevalence/libmerlin (4bf6228). This implementation 43 | // is buggy, for example we had to change FLAG_M to FLAG_C in line 178 of 44 | // merlin.c for it to work. Production code will have to fix that Merlin 45 | // implementation. 46 | 47 | namespace Merlin { 48 | inline void bytesFromInt(unsigned char *buf, size_t num) { // simple serialization, big endian 49 | for (int i=0; i< sizeof(size_t); i++) { 50 | buf[i] = (unsigned char) num & 0xff; 51 | num >>= 8; 52 | } 53 | } 54 | inline size_t intFromBytes(unsigned char *buf) { 55 | int num = 0; 56 | for (int i=0; i< sizeof(size_t); i++) { 57 | num += ((size_t)buf[i]) << (8*i); 58 | } 59 | return num; 60 | } 61 | 62 | struct MerlinBPctx { // A wrapper object around the "C" merlin_transcript 63 | merlin_transcript mctx; 64 | explicit MerlinBPctx(const merlin_transcript& m): mctx(m) {} 65 | explicit MerlinBPctx(const std::string& label=std::string()) { 66 | merlin_transcript_init(&mctx, (const unsigned char *)label.data(), label.size()); 67 | } 68 | 69 | // Add a labeled scalar to the current state 70 | void processScalar(const std::string& label, const CRV25519::Scalar& s) { 71 | merlin_transcript_commit_bytes(&mctx, 72 | (const unsigned char *)label.data(), label.size(), 73 | s.dataBytes(), crypto_core_ed25519_SCALARBYTES); 74 | } 75 | void processScalarVector(const std::string& label, CRV25519::Scalar* v, int n) { 76 | if (n<=0) return; 77 | merlin_transcript_commit_bytes(&mctx, 78 | (const unsigned char *)label.data(), label.size(), 79 | v[0].dataBytes(), crypto_core_ed25519_SCALARBYTES); 80 | for (int i=1; i 147 | newBlindingFactors(const std::string& label, size_t n, 148 | const CRV25519::Scalar* xes, const CRV25519::Scalar* ys=nullptr) { 149 | merlin_rng mrng; 150 | merlin_rng_init(&mrng, &mctx); 151 | 152 | // a large enough buffer 153 | unsigned char buf[2*crypto_core_ed25519_NONREDUCEDSCALARBYTES]; 154 | 155 | if (n>0) { 156 | merlin_rng_commit_witness_bytes(&mrng, (const uint8_t*)label.data(), label.size(), 157 | (const uint8_t*) xes[0].dataBytes(), crypto_core_ed25519_SCALARBYTES); 158 | for (size_t i=1; i twoScalars; 183 | crypto_core_ed25519_scalar_reduce(twoScalars.first.bytes, buf); 184 | crypto_core_ed25519_scalar_reduce(twoScalars.second.bytes, 185 | buf +crypto_core_ed25519_NONREDUCEDSCALARBYTES); 186 | return twoScalars; 187 | } 188 | }; 189 | } /* end of namespace MERLIN */ 190 | #endif // ifndef _MERLIN_H_ 191 | 192 | -------------------------------------------------------------------------------- /include/pedersen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _PEDERSEN_HPP_ 2 | #define _PEDERSEN_HPP_ 3 | /* ceddersen.hpp - Pedersen commitments 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | 29 | // The only lines in this file that are specific to our scalar space being 30 | // modulo the group order of Curve 25519 are this include and the typedefs 31 | // of scalar and point below. Hopefully this will make it easier to extend 32 | // the scope of this code also to other groups, should we want to do it in 33 | // the future. 34 | #include "scalar25519.hpp" 35 | 36 | /* Pedersen vector-commitments are defined relative to some generators. 37 | * In our application we have the "base" generator and in addition two 38 | * vectors of "G" and "H" generators. Specifically, each context object 39 | * has some string tag (to specify the context for it), and an optional 40 | * key. The generators are then computed as 41 | * 42 | * G_i = hashToCurve(tag+"G"+str(i), key) 43 | * H_i = hashToCurve(tag+"H"+str(i), key) 44 | * 45 | * A plaintext to be committed to is a vector in Z_P^{\leq N}, specified 46 | * by a map { j_1->a_1, j_2->a_2, ... j_n->a_n } where the j_i's are 47 | * (disjoint, sorted) indexesand the a's are scalars. 48 | * A "G-commitment"/"H-commitment" to this plaintext is a point 49 | * 50 | * C_G = r*base + \sum_i a_{j_i}*G_{j_i} 51 | * C_H = r*base + \sum_i b_{j_i}*H_{j_i} 52 | * 53 | * and we also consider double-commitments to two such plaintext vectors, 54 | * which is just a sum of a G-commitment and an H-commitment: 55 | * 56 | * C = base*r + \sum_i a_{j_i}*G_{j_i} + \sum_i b_{j_i}*H_{j_i} 57 | * 58 | * where both range over the same set of indexes. 59 | * 60 | * We also use commitments with yet another generator F, where we have 61 | * C_G = r*base + alpha*F + \sum_i a_{j_i}*G_{j_i} 62 | * C = r*base + alpha*F + \sum_i a_{j_i}*G_{j_i} + \sum_i b_{j_i}*H_{j_i} 63 | * These commitments are used in a context where the verifier supplies F, 64 | * after seeing some "F free" commitments. 65 | **/ 66 | #include 67 | #include "scalar25519.hpp" 68 | #include "point25519.hpp" 69 | 70 | namespace DLPROOFS { // FIXME: what namespace should we use here? 71 | using CRV25519::Scalar, CRV25519::Point, CRV25519::hashToCurve; 72 | 73 | // Some utility functions 74 | 75 | inline size_t next2power(size_t v) { 76 | v--; 77 | for (size_t i=1; i> i; 79 | return v+1; 80 | } 81 | 82 | // returns the smallest ell such that 2^{ell} >= n 83 | inline size_t log2roundUp(size_t n) { 84 | for (size_t ell=0; ell= n) 86 | return ell; 87 | } 88 | return sizeof(size_t)*8; 89 | } 90 | 91 | // compute \sum_i Gi*xi (code in pedersen.cpp) 92 | Point multiExp(const Point* Gs, size_t n, const Scalar* xes); 93 | 94 | // set gi *= x for all i 95 | void multiBaseOneExp(Point* Gs, size_t n, const Scalar& x); 96 | 97 | // with Gs=(Gs1|Gs2), set Gs1 += Gs2 * x 98 | void foldGenerators(Point* Gs, size_t n, const Scalar& x, size_t nOver2); 99 | 100 | // Fills vec with all the subset products of the basis scalars, times a 101 | void subsetProduct(Scalar *vec, size_t n, 102 | const Scalar* basis, size_t basisLen, const Scalar& a); 103 | 104 | // compute \sum_i Gi*zi, where the zi's are the subset products of the xi's 105 | // times a. If offsets are specified then return \sum_i Gi*(zi+offsets[i])[i]). 106 | Point expSubsetProduct(const Point* Gs, size_t n, const Scalar* xes, 107 | const Scalar& a, Scalar* offsets=nullptr); 108 | 109 | // old version modifies the generators in Gs and returns the result in Gs[0] 110 | void expSubsetProduct2(Point* Gs, size_t n, const Scalar* xes); 111 | 112 | // Functions for commitment/de-commitment 113 | 114 | inline Point commit(const Point* Gs, const Scalar* xes, size_t n, const Scalar& r, 115 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()) { 116 | return Point::base()*r + F*alpha + multiExp(Gs, n, xes); 117 | } 118 | inline bool verifyCom(const Point& c, const Point* Gs, const Scalar* xes, size_t n, 119 | const Scalar& r, const Point& F=Point::identity(), const Scalar& alpha=Scalar()) { 120 | return (commit(Gs, xes, n, r, F, alpha)==c); 121 | } 122 | 123 | inline Point commit2(const Point* Gs, const Scalar* xes, 124 | const Point* Hs, const Scalar* ys, size_t n, const Scalar& r, 125 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()) { 126 | return Point::base()*r + F*alpha + multiExp(Gs, n, xes) + multiExp(Hs, n, ys); 127 | } 128 | inline bool verifyCom2(const Point& c, const Point* Gs, const Scalar* xes, 129 | const Point* Hs, const Scalar* ys, size_t n, const Scalar& r, 130 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()) { 131 | return (commit2(Gs, xes, Hs, ys, n, r, F, alpha)==c); 132 | } 133 | 134 | // A Pedersen context defines the generators to use 135 | struct PedersenContext { 136 | std::string tag; 137 | //std::vector key; 138 | explicit PedersenContext(const std::string& t=std::string()): tag(t) {}; 139 | 140 | const Point getG(int i) const { // returns G_i 141 | std::string label = tag+"G"+std::to_string(i); 142 | return hashToCurve((unsigned char*)label.data(), label.size()); 143 | } 144 | const Point getH(int i) const { // returns H_i 145 | std::string label = tag+"H"+std::to_string(i); 146 | return hashToCurve((unsigned char*)label.data(), label.size()); 147 | } 148 | }; 149 | 150 | } // end of namespace DLPROOFS 151 | #endif // ifndef _PEDERSEN_HPP_ 152 | -------------------------------------------------------------------------------- /include/point25519.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _POINT_25519_HPP_ 2 | #define _POINT_25519_HPP_ 3 | /* point25519.hpp - a thin layer around libsodium low-level interfaces 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | #include 29 | extern "C" { 30 | #include 31 | } 32 | #include "scalar25519.hpp" 33 | 34 | namespace CRV25519 { 35 | 36 | // A class that holds a point on the curve 37 | class Point { 38 | static Point init(); // Initialize libsodium and the constants 39 | static Point identityPoint, basePoint; 40 | public: 41 | static size_t counter, timer; // used for profiling and performance measurements 42 | unsigned char bytes[crypto_core_ed25519_BYTES]; 43 | 44 | Point() { *this = identityPoint; } 45 | 46 | const unsigned char* dataBytes() const { return bytes; } 47 | 48 | // isValid returns true if a point is on the ed25519 curve, non-zero, 49 | // on the main subgroup, and of small order 50 | bool isValid() const { 51 | return crypto_core_ed25519_is_valid_point(bytes) == 1; 52 | } 53 | 54 | // returns true if two points are equal 55 | bool operator==(const Point& other) const { 56 | return sodium_memcmp(bytes, other.bytes, crypto_core_ed25519_BYTES) == 0; 57 | } 58 | bool operator!=(const Point& other) const { return !(*this == other); } 59 | 60 | Point& randomize() { 61 | crypto_core_ed25519_random(bytes); 62 | return *this; 63 | } 64 | 65 | // Compute the sum of two elliptic curve points, exception on failure 66 | Point& operator+=(const Point& other) { 67 | if (other==identity()) 68 | return *this; 69 | if (*this == identity()) 70 | *this = other; 71 | else { 72 | auto res = crypto_core_ed25519_add(bytes, bytes, other.bytes); 73 | if (res != 0) { 74 | throw std::runtime_error("failed to perform point addition, err#="+std::to_string(res)); 75 | } 76 | } 77 | return *this; 78 | } 79 | Point operator+(const Point& other) const { return Point(*this).operator+=(other); } 80 | 81 | // Compute the difference of two elliptic curve points, exception on failure 82 | Point& operator-=(const Point& other) { 83 | if (other==identity()) 84 | return *this; 85 | auto res = crypto_core_ed25519_sub(bytes, bytes, other.bytes); 86 | if (res != 0) { 87 | throw std::runtime_error("failed to perform point subtraction, err#="+std::to_string(res)); 88 | } 89 | return *this; 90 | } 91 | Point operator-(const Point& other) const { return Point(*this).operator-=(other); } 92 | 93 | // Computes the product of a scalar with a point 94 | Point& operator*=(const Scalar& n) { 95 | if (*this==identity() || n.isZero()) 96 | *this = identity(); 97 | else { 98 | auto start = std::chrono::steady_clock::now(); 99 | auto res = crypto_scalarmult_ed25519_noclamp(bytes, n.bytes, bytes); 100 | if (res != 0) { 101 | throw std::runtime_error("failed to perform scalar multiplication, err#="+std::to_string(res)); 102 | } 103 | counter++; // count exponentiations 104 | auto end = std::chrono::steady_clock::now(); 105 | timer += std::chrono::duration_cast(end - start).count(); 106 | } 107 | 108 | return *this; 109 | } 110 | Point operator*(const Scalar& s) const { return Point(*this).operator*=(s); } 111 | 112 | static const Point& identity() { return identityPoint; } 113 | static const Point& base() { return basePoint; } 114 | }; 115 | 116 | // randomPoint is a factory method, returning a random group element 117 | inline Point randomPoint() { return Point().randomize(); } 118 | 119 | // I/O 120 | inline std::ostream& operator<<(std::ostream& os, const Point& p) { 121 | os.write((const char*)p.dataBytes(), crypto_core_ed25519_BYTES); 122 | return os; 123 | } 124 | inline std::istream& operator>>(std::istream& is, Point& p) { 125 | is.read((char*)p.bytes, crypto_core_ed25519_BYTES); 126 | return is; 127 | } 128 | 129 | inline Point operator*(const Scalar& n, const Point& p) { return p*n; } 130 | 131 | // Initialize a point to G*n from a scalar n, a factory method 132 | inline Point baseTimesScalar(const Scalar& n) { 133 | Point pp; 134 | auto res = crypto_scalarmult_ed25519_base_noclamp(pp.bytes, n.bytes); 135 | if (res != 0) { 136 | throw std::runtime_error("failed to perform scalar multiplication by base, err#="+std::to_string(res)); 137 | } 138 | return pp; 139 | } 140 | 141 | // Hash arbitrary byte array to the curve 142 | inline Point hashToCurve(unsigned char* bytes, int len, 143 | unsigned char* key=nullptr, int keylen=0) { 144 | unsigned char h[crypto_core_ed25519_UNIFORMBYTES]; 145 | crypto_generichash(h, sizeof h, bytes, len, key, keylen); 146 | Point pp; 147 | crypto_core_ed25519_from_uniform(pp.bytes, h); 148 | return pp; 149 | } 150 | } /* end of namespace CRV25519 */ 151 | #endif // ifndef _POINT_25519_HPP_ 152 | -------------------------------------------------------------------------------- /include/regevEnc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _REGEVENC_HPP_ 2 | #define _REGEVENC_HPP_ 3 | /* regevEnc.hpp - a variant of Regev encryption with PVW packing 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | #include 29 | 30 | #include "algebra.hpp" 31 | 32 | //#define DEBUGGING 33 | 34 | namespace REGEVENC { 35 | struct KeyParams; 36 | 37 | // The global key for our Regev encrypiton includes the variour params, 38 | // the CRS k-by-m matrix A over and the ell*enn-by-emm matrix B with enn 39 | // public keys (both over Z_P). 40 | class GlobalKey { 41 | static ALGEBRA::BigInt Pmod; 42 | static ALGEBRA::Scalar deltaScalar; // approx P^{1/ell} 43 | static ALGEBRA::BigInt delta2ellm1; // Delta^{ell-1} 44 | static ALGEBRA::Element gElement; // (1,Delta,...,Delta^{ell-1}) 45 | static ALGEBRA::Element initPdeltaG(); // a function to initialize P,delta,g 46 | public: 47 | static const ALGEBRA::BigInt& P() { return Pmod; } 48 | static const ALGEBRA::Scalar& delta() { return deltaScalar; } 49 | static const ALGEBRA::BigInt& delta2ellMinus1() { return delta2ellm1; } 50 | static const ALGEBRA::Element& g() { return gElement; } 51 | 52 | // Some parameters are hard-wired, others are set at runtime 53 | static constexpr int pSize=252; // number of bits in P 54 | static constexpr int ell=2; // redundancy parameter, # dimensions per party 55 | static constexpr int rho=2; // secre-key, encryption randomness in [+-(2^{rho}-1)] 56 | 57 | std::string tag; // a string to tag this public key 58 | 59 | int enn; // # of parties 60 | int tee; // threshold, one more than # of corrupted, < enn/2 61 | int kay; // dimension of LWE-secrets (over GF(P^ell)) 62 | #ifndef DEBUGGING 63 | int sigmaEnc1=93; // keygen, encryption small noise in [+-(2^{sigmaEnc1}-1)] 64 | int sigmaEnc2=113; // encryption large noise in [+-(2^{sigmaEnc2}-1)] 65 | #else 66 | int sigmaEnc1=3; // keygen, encryption small noise in [+-(2^{sigmaEnc1}-1)] 67 | int sigmaEnc2=13; // encryption large noise in [+-(2^{sigmaEnc2}-1)] 68 | #endif // ifndef DEBUGGING 69 | 70 | size_t nPks; // number of GF(P^ell) public keys that are stored in B 71 | ALGEBRA::EMatrix A, B; // The matrix M = (A / B) 72 | unsigned char Ahash[32]; // fingerprint of the CRS A 73 | unsigned char Bhash[32]; // fingerprint of the key B 74 | 75 | GlobalKey() = delete; 76 | GlobalKey(const std::string tg, const KeyParams &prms, 77 | const ALGEBRA::EMatrix* crs=nullptr); // optional - a pre-selected CRS 78 | 79 | const unsigned char* const crsHash() const {return Ahash;} 80 | const unsigned char* const keyHash() const {return Bhash;} 81 | void setKeyHash(); // hash the matrix B 82 | 83 | bool operator==(const GlobalKey& other) const {return tag==other.tag;} 84 | bool operator!=(const GlobalKey& other) const {return !(*this==other);} 85 | 86 | // The actual implementation of key-generation 87 | void internalKeyGen(ALGEBRA::EVector& sk, ALGEBRA::EVector& pk, ALGEBRA::EVector& noise) const; 88 | 89 | // generate a new key-pair, returns (sk,pk) and optionally also noise, 90 | // each an ell-by-something matrix 91 | typedef std::pair< ALGEBRA::EVector, ALGEBRA::EVector > KeyPair; 92 | KeyPair genKeys(ALGEBRA::EVector* n=nullptr) const { 93 | KeyPair ret; 94 | if (n != nullptr) 95 | internalKeyGen(ret.first, ret.second, *n); 96 | else { 97 | ALGEBRA::EVector noise; 98 | internalKeyGen(ret.first, ret.second, noise); 99 | } 100 | return ret; 101 | } 102 | 103 | // Add the generated pk to the global key and return its index 104 | size_t addPK(const ALGEBRA::EVector& pk); 105 | 106 | // Implementation of encryption, ctx1=CRS x r +e1, ctxt2=PK x r +e2 +g*x 107 | void internalEncrypt(ALGEBRA::EVector& ctxt1, ALGEBRA::EVector& ctxt2, 108 | const ALGEBRA::SVector& ptxt, ALGEBRA::EVector& r, 109 | ALGEBRA::EVector& e1, ALGEBRA::EVector& e2) const; 110 | 111 | // Encrypt a vector of plaintext scalars, return ct0,ct1 and optionally 112 | // also the randomness and noise that were used in encryption 113 | typedef std::pair< ALGEBRA::EVector, ALGEBRA::EVector > CtxtPair; 114 | CtxtPair encrypt(const ALGEBRA::SVector& ptxt, 115 | ALGEBRA::EVector& r, CtxtPair& e) const { 116 | CtxtPair ct; 117 | internalEncrypt(ct.first, ct.second, ptxt, r, e.first, e.second); 118 | return ct; 119 | } 120 | CtxtPair encrypt(const ALGEBRA::SVector& ptxt) const { 121 | ALGEBRA::EVector r; 122 | CtxtPair e; 123 | return encrypt(ptxt, r, e); 124 | } 125 | 126 | // Decote the plaintext scalar (and optionally also the noise) 127 | // from the noisy plaintext 128 | ALGEBRA::Scalar decodePtxt(ALGEBRA::Element& noisyPtxt, 129 | ALGEBRA::Element* noise=nullptr) const; 130 | 131 | // The actual implementation of decryption 132 | void internalDecrypt(ALGEBRA::Scalar& ptxt, ALGEBRA::Element& noise, 133 | const ALGEBRA::EVector& sk, int idx, 134 | const ALGEBRA::EVector& ct1,const ALGEBRA::EVector& ct2)const; 135 | 136 | // Decrypts a ciphertext, returning ptxt and optioanlly the noise. 137 | // This function gets the idx of this specific secret key in the 138 | // global key, and it decrypts the relevant part of the ciphertext. 139 | ALGEBRA::Scalar decrypt(const ALGEBRA::EVector& sk, int idx, 140 | const CtxtPair& ctxt, ALGEBRA::Element* noise=nullptr) { 141 | ALGEBRA::Scalar pt; 142 | if (noise != nullptr) 143 | internalDecrypt(pt, *noise, sk, idx, ctxt.first, ctxt.second); 144 | else { 145 | ALGEBRA::Element noise2; 146 | internalDecrypt(pt, noise2, sk, idx, ctxt.first, ctxt.second); 147 | } 148 | return pt; 149 | } 150 | }; 151 | 152 | // Select a -1/0/1 scalar with Pr[0]=1/2 and Pr[+-1]=1/4 153 | class ZeroOneScalar { 154 | public: 155 | ALGEBRA::Scalar& randomize(ALGEBRA::Scalar& s) const { 156 | size_t x = ALGEBRA::randBitsize(2); // two random bits 157 | conv(s, (x&1)-(x>>1)); // return their difference 158 | return s; 159 | } 160 | ALGEBRA::Scalar randomize() const { 161 | ALGEBRA::Scalar s; 162 | return randomize(s); 163 | } 164 | }; 165 | 166 | // Select a -1/0/1 scalar with Pr[0]=1/2 and Pr[+-1]=1/4 167 | class ZeroOneElement { 168 | public: 169 | ALGEBRA::Element& randomize(ALGEBRA::Element& e) const { 170 | size_t x = ALGEBRA::randBitsize(GlobalKey::ell*2); // 2*ell random bits 171 | ALGEBRA::SVector v; 172 | ALGEBRA::resize(v,GlobalKey::ell); 173 | for (size_t i=0; i>1)&1)); // -1/0/1 scalar 175 | x >>= 2; 176 | } 177 | ALGEBRA::conv(e,v); 178 | return e; 179 | } 180 | ALGEBRA::Element randomize() const { 181 | ALGEBRA::Element e; 182 | return randomize(e); 183 | } 184 | }; 185 | 186 | // Select a scalar in the range [+-(2^n -1)], n<=251, almost uniformly 187 | // except the probability of zero is twice as large (namely 2^{-n}). 188 | // This implementation is constant time (maybe). 189 | class BoundedSizeScalar { 190 | public: 191 | int bitSize; 192 | BoundedSizeScalar() = delete; 193 | explicit BoundedSizeScalar(int n): bitSize(n) {} 194 | 195 | ALGEBRA::Scalar& randomize(ALGEBRA::Scalar& s) const { 196 | ALGEBRA::BigInt zzs[2]; 197 | ALGEBRA::randBitsize(zzs[0], bitSize); 198 | zzs[1] = -zzs[0]; 199 | conv(s, zzs[ALGEBRA::randomBit()]); // convert to a Scalar mod p 200 | return s; 201 | } 202 | ALGEBRA::Scalar randomize() const { 203 | ALGEBRA::Scalar s; 204 | return randomize(s); 205 | } 206 | }; 207 | class BoundedSizeElement { 208 | public: 209 | int bitSize; 210 | BoundedSizeElement() = delete; 211 | explicit BoundedSizeElement(int n): bitSize(n) {} 212 | 213 | ALGEBRA::Element& randomize(ALGEBRA::Element& e) const { 214 | BoundedSizeScalar bss(bitSize); 215 | ALGEBRA::SVector v; 216 | ALGEBRA::resize(v,GlobalKey::ell); 217 | for (size_t i=0; i 27 | #include 28 | #include 29 | #include 30 | #include 31 | extern "C" { 32 | #include 33 | } 34 | 35 | namespace CRV25519 { 36 | // Default randomize object, different implementation will derive 37 | // from it and override the default operator() method 38 | class RandomScalar { 39 | public: 40 | virtual void operator()(unsigned char* data) const { 41 | crypto_core_ed25519_scalar_random(data); 42 | } 43 | }; 44 | 45 | // A class that holds a scalar (modulo P = 2^{252}+ZZZ) 46 | class Scalar { 47 | public: 48 | unsigned char bytes[crypto_core_ed25519_SCALARBYTES]; 49 | 50 | Scalar() { std::memset(bytes, 0, crypto_core_ed25519_SCALARBYTES); } 51 | 52 | const unsigned char* dataBytes() const { return bytes; } 53 | 54 | // returns true if two scalars are equal 55 | bool operator==(const Scalar& other) const { 56 | return sodium_memcmp(bytes, other.bytes, 32) == 0; 57 | } 58 | bool operator!=(const Scalar& other) const { return !(*this == other); } 59 | bool isZero() const { return Scalar()==*this; } 60 | 61 | // select a random scalar in the range [0, P), P is group order 62 | Scalar& randomize(const RandomScalar& r=RandomScalar()) { 63 | r(bytes); 64 | return *this; 65 | } 66 | 67 | // Computes the multiplicative inverse of a scalar mod P, where P 68 | // is the order of the main subgroup 69 | Scalar& invert() { 70 | auto res = crypto_core_ed25519_scalar_invert(bytes, bytes); 71 | if (res != 0) { 72 | throw std::runtime_error("failed to perform scalar inversion, err#="+std::to_string(res)); 73 | } 74 | return *this; 75 | } 76 | 77 | // Computes the additive inverse of a scalar mod P, where P 78 | // is the order of the main subgroup 79 | Scalar& negate() { 80 | crypto_core_ed25519_scalar_negate(bytes, bytes); 81 | return *this; 82 | } 83 | 84 | // Add two scalars mod P 85 | Scalar& operator+=(const Scalar& other) { 86 | crypto_core_ed25519_scalar_add(bytes, bytes, other.bytes); 87 | return *this; 88 | } 89 | Scalar operator+(const Scalar& other) const { 90 | return Scalar(*this).operator+=(other); 91 | } 92 | 93 | // Subtract two scalars mod P 94 | Scalar& operator-=(const Scalar& other) { 95 | crypto_core_ed25519_scalar_sub(bytes, bytes, other.bytes); 96 | return *this; 97 | } 98 | Scalar operator-(const Scalar& other) const { 99 | return Scalar(*this).operator-=(other); 100 | } 101 | 102 | // Computes the product of two scalars mod P 103 | Scalar& operator*=(const Scalar& other) { 104 | crypto_core_ed25519_scalar_mul(bytes, bytes, other.bytes); 105 | return *this; 106 | } 107 | Scalar operator*(const Scalar& other) const { 108 | return Scalar(*this).operator*=(other); 109 | } 110 | 111 | // Compute division 112 | Scalar& operator/=(const Scalar& other) { 113 | Scalar tmp = other; 114 | tmp.invert(); 115 | return (*this *= tmp); 116 | } 117 | Scalar operator/(const Scalar& other) const { 118 | return Scalar(*this).operator/=(other); 119 | } 120 | 121 | // Computes *this modulo |other|. Both arguments are interpreted 122 | // as signed integers in the range [-P/2,P/2), the result is set 123 | // in the range [-|other|/2, |other|/2), then mapped to Z_P. 124 | // FIXME (SH): We really should not have this method, it is a hack to 125 | // avoid having to implement stuff myself, but it does not belong here 126 | Scalar& operator%=(const Scalar& other); 127 | 128 | // Convert a signed integer to a scalar (useful for tests and debugging) 129 | Scalar& setInteger(long n) { 130 | std::memset(bytes, 0, crypto_core_ed25519_SCALARBYTES); // reset to zero 131 | bool negated = (n < 0); 132 | if (negated) { // make it positive 133 | n = -n; 134 | } 135 | for (int i=0; i < sizeof(long); i++) { 136 | bytes[i] = (unsigned char)(n & 0xff); 137 | n >>= 8; 138 | } 139 | if (negated) { 140 | negate(); 141 | } 142 | return *this; 143 | } 144 | }; 145 | 146 | // A factory method, returning a random scalar in the 147 | // range [0, P), where P is the order of the main subgroup 148 | inline Scalar randomScalar(const RandomScalar& r=RandomScalar()) { 149 | return Scalar().randomize(r); 150 | } 151 | 152 | // I/O 153 | inline std::ostream& operator<<(std::ostream& os, const Scalar& s) { 154 | os.write((const char*)s.dataBytes(), crypto_core_ed25519_SCALARBYTES); 155 | return os; 156 | } 157 | inline std::istream& operator>>(std::istream& is, Scalar& s) { 158 | is.read((char*)s.bytes, crypto_core_ed25519_SCALARBYTES); 159 | return is; 160 | } 161 | 162 | inline Scalar inverseOf(const Scalar& a) { return Scalar(a).invert(); } 163 | inline Scalar negationOf(const Scalar& a) {return Scalar(a).negate(); } 164 | 165 | // A helper function, currently a naive implementation 166 | Scalar innerProduct(const Scalar* a, const Scalar* b, size_t n); 167 | 168 | /****** A few usefule randomizing objects **********/ 169 | 170 | // Select a -1/0/1 scalar with Pr[0]=1/2 and Pr[+-1]=1/4. Note that there are 171 | // faster ways of choosing many such scalars then repeated calls to this object. 172 | // This implementation is NOT constant time. 173 | class ZeroOneScalar: public RandomScalar { 174 | public: 175 | void operator()(unsigned char* data) const override { 176 | unsigned int r = randombytes_random(); 177 | // A random 32-bit word, we only use two bits of it 178 | if ((r & 1) != 0) { 179 | data[0] = 1; 180 | if ((r & 2) != 0) 181 | crypto_core_ed25519_scalar_negate(data, data); 182 | } 183 | } 184 | }; 185 | 186 | // Select a scalar in the range [+-(2^n -1)], n<=251, almost uniformly 187 | // except the probability of zero is twice as large (namely 2^{-n}). 188 | // This implementation is constant time (maybe). 189 | class BoundedSizeScalar: public RandomScalar { 190 | public: 191 | int nBytes; 192 | unsigned char mask; 193 | 194 | BoundedSizeScalar() = delete; 195 | explicit BoundedSizeScalar(int n) { 196 | if (n<1 || n>251) { 197 | throw std::runtime_error("illeagal bitsize for BoundedSizeScalar: "+std::to_string(n)); 198 | } 199 | // How many bytes to fit a number in [0, 2^n -1] 200 | nBytes = (7+n)/8; // = ceiling(n/8) 201 | 202 | // A mask to turn off excess bits at the top byte (if any) 203 | mask = ((unsigned char)0xff) >> ((8-n)&0x7); 204 | } 205 | 206 | void operator()(unsigned char* data) const override { 207 | unsigned char bufs[2][crypto_core_ed25519_SCALARBYTES]; 208 | std::memset(bufs, 0, sizeof bufs); 209 | randombytes_buf(bufs[0], nBytes); // fill 1st buffer with random bytes 210 | bufs[0][nBytes -1] &= mask; // zero out excess bits at the top 211 | crypto_core_ed25519_scalar_negate(bufs[1], bufs[0]); // negate 2nd buf 212 | 213 | // Copy one of the two buffers 214 | int bit = randombytes_random() & 1; 215 | std::memcpy(data, bufs[bit], crypto_core_ed25519_SCALARBYTES); 216 | } 217 | }; 218 | } /* end of namespace CRV25519 */ 219 | #endif // ifndef _SCALAR_25519_HPP_ 220 | -------------------------------------------------------------------------------- /include/shamir.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _SHAMIR_HPP_ 2 | #define _SHAMIR_HPP_ 3 | /* shamir.hpp - Shamir secret sharing 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | 28 | #include "algebra.hpp" 29 | 30 | namespace TOOLS { 31 | using ALGEBRA::Scalar, ALGEBRA::SVector, ALGEBRA::SMatrix; 32 | 33 | typedef std::set EvalSet; // the set of evaluation points 34 | 35 | // Holds parameters related to t-of-n secret sharing where the secret 36 | // is at evaluation point 0 and the n evaluation points are specified 37 | // in the constror 38 | struct SharingParams { 39 | int thr; // the threshold (= polynomial degree plus one) 40 | EvalSet evalPoints; // The set of all n evaluation points 41 | 42 | SMatrix H; // The parity-check matrix H 43 | // Considering a vector x representing the evluations at all the 44 | // points (0, i_1, i_2, ..., i_n) in order, where the i_j's are 45 | // specified by the set evalPoints, this is a valid sharing of 46 | // x[0] iff it satisfies H*x=0 47 | 48 | int t() const { return thr; } 49 | int n() const { return evalPoints.size(); } 50 | 51 | void computeKernel(); // compute the parity-check matrix H 52 | // H depends only on the set of all n evaluation points, not 53 | // on the specific t-subset chosen for reconstruction every time 54 | 55 | SharingParams() = default; 56 | SharingParams(const EvalSet& ev, int t): evalPoints(ev), thr(t) 57 | { computeKernel(); } 58 | 59 | // Compute random secret-sharings, the secret itself is returned in 60 | // the 1st entry of the vector v, followed by the shares in all the 61 | // evaluation points (in order). 62 | void newSharing(SVector& v, const Scalar& s) const; // random sharing of s 63 | void randomSharing(SVector& v) const{ // random sharing of a random scalar 64 | Scalar s; 65 | ALGEBRA::randomizeScalar(s); 66 | newSharing(v, s); 67 | } 68 | 69 | // Compute the lagrange coefficients for a size-t reconstruction 70 | // set (i.e. a t-subset of evalPoints). 71 | SVector lagrangeCoeffs(const EvalSet& recSet) const; 72 | 73 | // Recover the secret from its sharing at the given reconstruction set 74 | // Note that the reconstruction set is a proper subset of evalPoints. 75 | Scalar getSecret(const SVector& sharing, const EvalSet& recSet); 76 | }; 77 | 78 | } // end of namespace TOOLS 79 | #endif // #ifndef _SHAMIR_HPP_ 80 | -------------------------------------------------------------------------------- /include/ternaryMatrix.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _TERNARY_MATRIX_HPP_ 2 | #define _TERNARY_MATRIX_HPP_ 3 | /* ternaryMatrix.hpp - A 0/+-1 matrix 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | #include "regevEnc.hpp" // brings in NTL compatibility 29 | 30 | namespace ALGEBRA { 31 | 32 | // TernaryMatrix is an integer matrix over -1/0/1. 33 | // Each row of an n-by-m matrix M is represented by a binary vector 34 | // of length 2m. Every two bits x,y in this vector represent a trenary 35 | // number with (00)-> 0, (01)-> 1, (10)-> -1. 36 | // 37 | // This class offers a rather limited functionality, all you can do is 38 | // access individual elements, or multiply by one of SVector, EVector, 39 | // SMatrix, or EMatrix. 40 | 41 | struct TernaryMatrix { 42 | struct TrenaryRow { // a single -1/0/1 row 43 | size_t len; 44 | std::vector rep; // internal representation 45 | TrenaryRow(): len(0) {} 46 | 47 | size_t size() const { return len; } 48 | void resize(size_t m) { 49 | len = m; 50 | rep.resize((m+3)/4); 51 | } 52 | int operator[](size_t i) const { 53 | size_t j = i & 0x03; // index two bits in a byte 54 | int theByte = rep[i/4] >> (2*j); 55 | return (theByte&1) - ((theByte>>1)&1); 56 | // subtract one bit from the other, get a -1/0/1 value 57 | } 58 | int at(size_t i) const { 59 | if (i>=len) { 60 | std::string what = "TrenaryRow::range_check [" 61 | +std::to_string(i)+"] but len="+std::to_string(len); 62 | throw std::out_of_range(what); 63 | } 64 | return (*this)[i]; 65 | } 66 | }; 67 | std::vector rows; // representation: an array of rows 68 | 69 | size_t NumRows() const {return rows.size();} 70 | size_t NumCols() const { 71 | if (NumRows()==0) return 0; 72 | return rows[0].size(); 73 | } 74 | 75 | TernaryMatrix& resize(size_t n, size_t m) { 76 | rows.resize(n); 77 | for (auto& row : rows) row.resize(m); 78 | return *this; 79 | } 80 | 81 | // read-only access to individual rows 82 | const TrenaryRow& operator[](size_t i) const { return rows[i]; } 83 | const TrenaryRow& at(size_t i) const { return rows.at(i); } 84 | 85 | // fill with random 0/+-1 values, Pr[0]=1/2, Pr[+-1]=1/4 86 | TernaryMatrix& random(); 87 | TernaryMatrix& random(size_t n, size_t m) { 88 | resize(n,m); 89 | return random(); 90 | } 91 | 92 | // Set the value from the give nbits, assumes that sizeof(bits)>=ceil(n*m/4) 93 | // If the given bits are random then Pr[0]=1/2, Pr[+-1]=1/4 94 | TernaryMatrix& setFromBytes(unsigned char* bytes); 95 | TernaryMatrix& setFromBytes(unsigned char* bytes, size_t n, size_t m) { 96 | resize(n,m); 97 | return setFromBytes(bytes); 98 | } 99 | }; 100 | inline void resize(TernaryMatrix& mat, size_t n, size_t m) { // compatibility 101 | mat.resize(n,m); 102 | } 103 | inline TernaryMatrix randomTernaryMatrix(size_t n, size_t m) { // factory 104 | TernaryMatrix tMat; 105 | return tMat.random(n,m); 106 | } 107 | 108 | void leftVecMult(SVector& result, const SVector& v, const TernaryMatrix& mat); 109 | void rightVecMult(SVector& result, const TernaryMatrix& mat, const SVector& v); 110 | void leftMatMult(SMatrix& result, const SMatrix& mat1, const TernaryMatrix& mat2); 111 | void rightMatMult(SMatrix& result, const TernaryMatrix& mat1, const SMatrix& mat2); 112 | 113 | void leftVecMult(EVector& result, const EVector& v, const TernaryMatrix& mat); 114 | void rightVecMult(EVector& result, const TernaryMatrix& mat, const EVector& v); 115 | void leftMatMult(EMatrix& result, const EMatrix& mat1, const TernaryMatrix& mat2); 116 | void rightMatMult(EMatrix& result, const TernaryMatrix& mat1, const EMatrix& mat2); 117 | 118 | inline SVector operator*(const SVector& v, const TernaryMatrix& mat) { 119 | SVector res; 120 | leftVecMult(res, v, mat); 121 | return res; 122 | } 123 | inline SVector operator*(const TernaryMatrix& mat, const SVector& v) { 124 | SVector res; 125 | rightVecMult(res, mat, v); 126 | return res; 127 | } 128 | inline SMatrix operator*(const SMatrix& mat1, const TernaryMatrix& mat2) { 129 | SMatrix res; 130 | leftMatMult(res, mat1, mat2); 131 | return res; 132 | } 133 | inline SMatrix operator*(const TernaryMatrix& mat1, const SMatrix& mat2) { 134 | SMatrix res; 135 | rightMatMult(res, mat1, mat2); 136 | return res; 137 | } 138 | 139 | // TernaryEMatrix is an matrix M over GF(q^e), with power-basis 140 | // representation M = M0 + M1*X + ... + M_{e-1} X^{e-1}, where 141 | // each Mi is a TernaryMatrix. 142 | struct TernaryEMatrix { 143 | static size_t degree; // e = degree of GF(p^e) 144 | static Element X; // The ring element whose representation is X 145 | static void init(); 146 | // Set the degree and element X, must be called after 147 | // NTL::ZZ_pE is initialized 148 | 149 | std::vector Ms; // the internal representation 150 | 151 | size_t NumRows() const {return (Ms.empty()? 0 : Ms[0].NumRows());} 152 | size_t NumCols() const {return (Ms.empty()? 0 : Ms[0].NumCols());} 153 | TernaryEMatrix& resize(size_t n, size_t m) { 154 | Ms.resize(degree); 155 | for (size_t i=0; i=ceil(n*m/4) 185 | // If the given bits are random then Pr[0]=1/2, Pr[+-1]=1/4 186 | TernaryEMatrix& setFromBytes(unsigned char* bytes); 187 | TernaryEMatrix& setFromBytes(unsigned char* bytes, size_t n, size_t m) { 188 | resize(n,m); 189 | return setFromBytes(bytes); 190 | } 191 | }; 192 | 193 | void leftVecMult(EVector& result, const EVector& v, const TernaryEMatrix& mat); 194 | void rightVecMult(EVector& result, const TernaryEMatrix& mat, const EVector& v); 195 | void leftMatMult(EMatrix& result, const EMatrix& mat1, const TernaryEMatrix& mat2); 196 | void rightMatMult(EMatrix& result, const TernaryEMatrix& mat1, const EMatrix& mat2); 197 | 198 | inline EVector operator*(const EVector& v, const TernaryEMatrix& mat) { 199 | EVector res; 200 | leftVecMult(res, v, mat); 201 | return res; 202 | } 203 | inline EVector operator*(const TernaryEMatrix& mat, const EVector& v) { 204 | EVector res; 205 | rightVecMult(res, mat, v); 206 | return res; 207 | } 208 | inline EMatrix operator*(const EMatrix& mat1, const TernaryEMatrix& mat2) { 209 | EMatrix res; 210 | leftMatMult(res, mat1, mat2); 211 | return res; 212 | } 213 | inline EMatrix operator*(const TernaryEMatrix& mat1, const EMatrix& mat2) { 214 | EMatrix res; 215 | rightMatMult(res, mat1, mat2); 216 | return res; 217 | } 218 | 219 | } // end of namespace REGEVENC 220 | #endif // #ifndef _TERNARY_MATRIX_HPP_ 221 | -------------------------------------------------------------------------------- /include/tests.hpp: -------------------------------------------------------------------------------- 1 | namespace LWEVSS_TESTS { 2 | // These constans are used in the CMakeLists.txt under ./tests 3 | const char* passed = "Test passed"; 4 | const char* failed = "Test failed"; 5 | 6 | } // end of LWEVSS_TESTS namespace 7 | -------------------------------------------------------------------------------- /include/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_HPP_ 2 | #define _UTILS_HPP_ 3 | /* utils.hpp - Utility functions (only sum-of-four squares for now) 4 | * 5 | * Copyright (C) 2021, LWE-PVSS 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject 13 | * to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | **/ 26 | #include 27 | #include 28 | #include 29 | #include "constraints.hpp" 30 | #include "algebra.hpp" 31 | 32 | namespace ALGEBRA { 33 | typedef std::array TwoSqrts; 34 | typedef std::array FourSqrts; 35 | 36 | // Decomposes a prime, p=1 mod 4, to a sum of two squares 37 | TwoSqrts decomposeProbablePrime(BigInt p); 38 | 39 | // Decomposes a nonegative integer into a sum of four squares 40 | FourSqrts decompose4(BigInt n); 41 | 42 | inline int ceilDiv(int a, int b) { return (a+b-1)/b;} 43 | 44 | // Compute the norm of v as a bigInt (no modular reduction) 45 | BigInt normSquaredBI(BIVector& vv); 46 | BigInt normSquaredBigInt(const SVector& v); 47 | BigInt normSquaredBigInt(const EVector& v); 48 | BigInt normSquaredBigInt(const Element* v, size_t len); 49 | BigInt lInftyNorm(const EVector& v); 50 | 51 | inline std::set interval(int from, int to) { 52 | std::set intSet; 53 | for (int i=from; i" << balanced(x.second) << ", "; 77 | } 78 | return (st << "]"); 79 | } 80 | inline std::ostream& prettyPrint(std::ostream& st, const DLPROOFS::LinConstraint& c) { 81 | prettyPrint(st<<"{", c.terms) << ", " << balanced(c.equalsTo) << "}"; 82 | return st; 83 | } 84 | inline std::ostream& prettyPrint(std::ostream& st, const DLPROOFS::QuadConstraint& c) { 85 | st<<"{["; 86 | for (auto i : c.indexes) { st << i<< " "; } 87 | st << "], " << balanced(c.equalsTo) << "}"; 88 | return st; 89 | } 90 | 91 | inline void addEVec2Map(DLPROOFS::PtxtVec& mymap, const EVector& v, size_t offset=0) { 92 | std::pair tmp; 93 | tmp.first = offset + v.length()*scalarsPerElement() -1; 94 | auto it = mymap.end(); 95 | for (long i=v.length()-1; i>=0; --i) 96 | for (long j=scalarsPerElement()-1; j>=0; --j) { 97 | it = mymap.insert(it, tmp); 98 | conv(it->second, coeff(v[i],j)); 99 | tmp.first--; 100 | } 101 | } 102 | inline void addSVec2Map(DLPROOFS::PtxtVec& mymap, const SVector& v, size_t offset=0) { 103 | std::pair tmp; 104 | tmp.first = offset +v.length() -1; 105 | auto it = mymap.end(); 106 | for (long i=v.length()-1; i>=0; --i) { 107 | it = mymap.insert(it, tmp); 108 | conv(it->second, v[i]); 109 | tmp.first--; 110 | } 111 | } 112 | 113 | inline void addRange2Set(std::set& myset, size_t from, size_t len) { 114 | auto range = interval(from, from+len); 115 | myset.insert(range.begin(), range.end()); 116 | } 117 | 118 | } // end of namespace ALGEBRA 119 | 120 | inline std::ostream& operator<<(std::ostream& st, const std::set& intSet){ 121 | st << '{'; 122 | for (auto i: intSet) st << i << ' '; 123 | return st << '}'; 124 | } 125 | inline std::ostream& operator<<(std::ostream& st, const std::set& intSet){ 126 | st << '{'; 127 | for (auto i: intSet) st << i << ' '; 128 | return st << '}'; 129 | } 130 | 131 | #endif // ifndef _UTILS_HPP_ -------------------------------------------------------------------------------- /src/25519/curve25519.cpp: -------------------------------------------------------------------------------- 1 | /* curve25519.cpp - a thin layer around libsodium low-level interfaces 2 | * 3 | * Copyright (C) 2021, LWE-PVSS 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject 11 | * to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | * OTHER DEALINGS IN THE SOFTWARE. 23 | **/ 24 | #include // define std::min 25 | #include 26 | 27 | #include 28 | #include "scalar25519.hpp" 29 | #include "point25519.hpp" 30 | 31 | namespace CRV25519 { 32 | 33 | size_t Point::counter = 0; 34 | size_t Point::timer = 0; 35 | Point Point::basePoint; 36 | Point Point::identityPoint = Point::init(); // forcing a run of the init function 37 | 38 | Point Point::init() { 39 | static bool firstTime = true; // ensure that init only happens once 40 | // FIXME: not thread safe 41 | if (!firstTime) // return a dummy point 42 | return Point(); 43 | firstTime = false; 44 | 45 | int ret = sodium_init(); 46 | if (ret != 0) { 47 | throw std::runtime_error("libsodium failed to initialize, #errno="+std::to_string(ret)); 48 | } 49 | 50 | // initialize the point base 51 | Scalar one = Scalar().setInteger(1); 52 | ret = crypto_scalarmult_ed25519_base_noclamp(basePoint.bytes, one.bytes); 53 | if (ret != 0) { 54 | throw std::runtime_error("failed to initialize basePoint, #errno="+std::to_string(ret)); 55 | } 56 | 57 | // The modulus of the main group: 2^{252} + 27742317777372353535851937790883648493 58 | // NTL::ZZ_p::init((NTL::to_ZZ(1L)<<252) + NTL::conv("27742317777372353535851937790883648493")); 59 | 60 | return basePoint - basePoint; // this will be assigned to the global identityPoint 61 | } 62 | 63 | // A helper function, naive implementation 64 | Scalar innerProduct(const Scalar* a, const Scalar* b, size_t n) { 65 | Scalar ret; 66 | for (size_t i=0; i("27742317777372353535851937790883648493"); 77 | NTL::ZZ x = NTL::ZZFromBytes(bytes, crypto_core_ed25519_SCALARBYTES); 78 | NTL::ZZ y = NTL::ZZFromBytes(other.bytes, crypto_core_ed25519_SCALARBYTES); 79 | if (x >= P/2) x -= P; // map to the range [-P/2, P/2) 80 | if (y >= P/2) { 81 | y = abs(y-P); // map to the range [-P/2, P/2), then take absolute value 82 | } 83 | x %= y; // in [0,y-1] 84 | if (x > y/2) { 85 | x = P+x-y; // map to [-y/2,y/2), then map to an element in Z_P 86 | } 87 | BytesFromZZ(bytes, x, crypto_core_ed25519_SCALARBYTES); 88 | return *this; 89 | } 90 | 91 | 92 | } /* end of namespace CRV25519 */ 93 | -------------------------------------------------------------------------------- /src/algebra/foursquares.cpp: -------------------------------------------------------------------------------- 1 | /* foursquares.cpp - Lagraamge four squares theorem 2 | * 3 | * Copyright (C) 2021, LWE-PVSS 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject 11 | * to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | * OTHER DEALINGS IN THE SOFTWARE. 23 | **/ 24 | #include 25 | #include 26 | #include 27 | //#include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "NTL/ZZ.h" 35 | #include "utils.hpp" 36 | // This implementation is based on Peter Schorn's Python code from 37 | // https://schorn.ch/lagrange.html 38 | 39 | namespace ALGEBRA { 40 | 41 | // n = 10080 = 2^5 *3^2 *5 *7 is a "magic number", there are exactly 336 42 | // squares mod n. This is the smallest rate of squares for any n < 15840 43 | static constexpr int magicN = 10080; 44 | static std::set computeSquaresModMagic() { 45 | std::set squares; 46 | for (int i=0; i<=magicN/2; i++) { 47 | squares.insert((i*i) % magicN); 48 | } 49 | return squares; 50 | } 51 | // This will only be called when the program is first loaded 52 | static std::set squaresModMagic = computeSquaresModMagic(); 53 | 54 | // If maybeSquare returns false then n is certainly not a square. If it 55 | // returns true then n is likely a square, the fraction of non-suqre n's 56 | // for which it returns true is quite small (3 in a million or so). 57 | static bool maybeSquare(const NTL::ZZ& n) { 58 | if (n.SinglePrecision()) { // handle small n's 59 | NTL::ZZ sq = NTL::SqrRoot(n); 60 | return (sq*sq) == n; 61 | } 62 | int residue = n % magicN; 63 | if (squaresModMagic.find(residue)==squaresModMagic.end()) 64 | return false; // n is not a squae modulo magicN 65 | 66 | // ten small primes, co-prime with the "magic N" above 67 | for (int p: {11,13,17,19,23,29,31,37,41,43}) 68 | if (NTL::Jacobi(n, NTL::to_ZZ(p))==-1) 69 | return false; 70 | 71 | return true; // could not demonstrate that n is not a square 72 | } 73 | 74 | // returns a square-root of -1 mod p if found, otherwise returns zero. 75 | // Will only be called when p<281. 76 | static int iunit_small(int p) { 77 | if (p<281 && (p&3)==1) { 78 | for (long i=2; i<=p/2; i++) 79 | if ((i*i) % p == (p-1)) 80 | return i; 81 | } 82 | return 0; 83 | } 84 | 85 | /* Compute a square root of -1 modulo p if p is a prime and p%4 == 1. 86 | * If p itself is a square it returns sqrt(p). The boolean return value 87 | * signifies if p is a square. The ZZ return value is set to zero on 88 | * failure. 89 | */ 90 | static std::pair iunit(const NTL::ZZ& p) { 91 | if (maybeSquare(p)) { 92 | NTL::ZZ s = NTL::SqrRoot(p); 93 | if (s*s==p) // p is a square 94 | return std::make_pair(s, true); 95 | } 96 | std::pair ret(NTL::ZZ::zero(),false); 97 | if ((p & 7L) == 5L) { 98 | ret.first = NTL::PowerMod(NTL::to_ZZ(2), p/4, p); 99 | } else if (p<281) { 100 | ret.first = NTL::to_ZZ(iunit_small(NTL::conv(p))); 101 | } else if ((p & 3L) == 1L) { 102 | NTL::PrimeSeq s; // go over the first 60 primes 103 | s.reset(2); // start from 3 rather than 2 104 | for (long q=s.next(); q<281; q=s.next()) { 105 | auto qq = NTL::to_ZZ(q); 106 | if (NTL::Jacobi(qq,p) == -1) { 107 | ret.first = NTL::PowerMod(qq, (p-1)/4, p); 108 | } 109 | } 110 | } 111 | return ret; 112 | } 113 | 114 | // NTL::NextPrime(n) instead of nextProbablePrime(n), np(n) 115 | // NTL::NumBits(n) instead of floorLog2 116 | 117 | // Try to decompose a prime p, p%4==1, into a sum of two squares. 118 | // Returns <0,0> on failure 119 | TwoSqrts decomposeProbablePrime(NTL::ZZ p) { 120 | if ((p & 3) != 1) { 121 | throw std::runtime_error("decomposeProbablePrime: must have p=1 mod 4"); 122 | } 123 | auto [b, issqr] = iunit(p); 124 | if (issqr) // p is a square, return [0,sqrt(p)] 125 | return {NTL::ZZ::zero(), b}; 126 | 127 | if (NTL::IsZero(b)) // sqrt(-1) not found 128 | return {NTL::ZZ::zero(), NTL::ZZ::zero()}; 129 | 130 | // Now we have b^2 = -1 mod p, run Cornacchia's algorithm with d=1 131 | auto a = p; 132 | while (b * b > p) { // set (a,b) = (b, a % b) 133 | NTL::ZZ tmp = b; 134 | b = a % b; 135 | a = tmp; 136 | } 137 | return {b, a % b}; 138 | } 139 | 140 | 141 | // A dictionary of exceptional cases the search in decompose4 cannot handle 142 | typedef std::array ThreeSqrts; 143 | std::map decomposeExceptions = { 144 | { 2,{ 0, 1, 1}}, { 3,{ 1, 1, 1}}, { 10,{ 0, 1, 3}}, 145 | { 34,{ 3, 3, 4}}, { 58,{ 0, 3, 7}}, { 85,{ 0, 6, 7}}, 146 | { 130,{ 0, 3, 11}}, { 214,{ 3, 6, 13}}, { 226,{ 8, 9, 9}}, 147 | { 370,{ 8, 9, 15}}, { 526,{ 6, 7, 21}}, { 706,{15, 15, 16}}, 148 | { 730,{ 0, 1, 27}}, {1414,{ 6, 17, 33}}, {1906,{13, 21, 36}}, 149 | {2986,{21, 32, 39}}, {9634,{56, 57, 57}} 150 | }; 151 | 152 | static FourSqrts& sortScaled(FourSqrts& list, const NTL::ZZ& v) { 153 | std::sort(list.begin(), list.end()); 154 | for (auto& x: list) x *= v; 155 | return list; 156 | } 157 | 158 | /* Decompose an integer n>=0 into a sum of up to four squares. Returns a 159 | * 4-array of integers, [-1,-1,-1,-1] if the search loop fails. Currently 160 | * there are no known inputs where the search loop fails, but there is 161 | * no known proof. 162 | */ 163 | FourSqrts decompose4(NTL::ZZ n) { 164 | if (n<0) { 165 | throw std::runtime_error("decompose4: must have n>=0"); 166 | } 167 | if (n <= 1) 168 | return {NTL::ZZ::zero(),NTL::ZZ::zero(),NTL::ZZ::zero(),n}; 169 | 170 | // Remove powers of 4 from n and remember them in v 171 | NTL::ZZ v = NTL::to_ZZ(1); 172 | while ((n & 3) == 0) { 173 | n >>= 2; // n = n / 4 174 | v <<= 1; // v = 2v 175 | } 176 | // now original-n = n * v^2 177 | 178 | NTL::ZZ sq = NTL::SqrRoot(n); 179 | auto p = n - (sq*sq); 180 | if (NTL::IsZero(p)) { // n is a square, sq=sqrt(n), p=0 181 | return {p, p, p, sq*v}; 182 | } 183 | 184 | // a return value that signifies failure 185 | const FourSqrts failed = {NTL::to_ZZ(-1),NTL::to_ZZ(-1),NTL::to_ZZ(-1),NTL::to_ZZ(-1)}; 186 | 187 | FourSqrts ret; // used as scratch space 188 | 189 | if (((n & 3) == 1) && NTL::ProbPrime(n)) { // n is prime, n % 4 = 1 190 | auto [s, r] = decomposeProbablePrime(n); 191 | if (!NTL::IsZero(r)) { // r==0 means that n was not a prime 192 | ret = {NTL::ZZ::zero(), NTL::ZZ::zero(), s, r}; 193 | return sortScaled(ret, v); 194 | } 195 | } 196 | 197 | // Recall that we have sq = floor(sqrt(n)) and p = n - sq^2 198 | 199 | NTL::ZZ delta; 200 | if ((n & 7) == 7) {// Need 4 squares, subtract largest square delta^2 201 | // such that (n > delta^2) and (delta^2 % 8 != 0) 202 | delta = sq; 203 | n = p; 204 | if ((sq & 3) == 0) { 205 | delta -= 1; 206 | n += (delta<<1) + 1; 207 | } 208 | // sq % 4 = 0 -> delta % 8 in [3, 7] 209 | // -> delta^2 % 8 = 1 -> n % 8 = 6 210 | // sq % 4!= 0 -> delta % 8 in [1, 2, 3, 5, 6, 7] 211 | // -> delta^2 % 8 in [1, 4] -> n % 8 in [3, 6] 212 | // this implies n % 4 != 0,1,2, so n cannot be a sum of two squares 213 | 214 | sq = NTL::SqrRoot(p); 215 | p = n - (sq*sq); 216 | // sq^2 != n since cannot be a sum of two squares 217 | } 218 | 219 | // At this point we have sq = floor(sqrt(n)) and (n % 8 != 7) and 220 | // (n % 4 != 0) and moreover the original_n = v^2 * (n + delta^2). 221 | // This implies that n is a sum of three squares. 222 | 223 | // First check whether n is one of the special cases the rest 224 | // of the algorithm could not handle 225 | if (n <= 9634) { 226 | auto it = decomposeExceptions.find(NTL::conv(n)); 227 | if (it != decomposeExceptions.end()) { 228 | auto [x,y,z] = it->second; 229 | ret = {delta, NTL::to_ZZ(x), NTL::to_ZZ(y), NTL::to_ZZ(z)}; 230 | return sortScaled(ret, v); 231 | } 232 | } 233 | // Now perform search distinguishing two cases noting that n % 4 != 0 234 | 235 | // Case 1: n % 4 = 3, n = x^2 + 2*p, p % 4 = 1, 236 | // if p is prime, p=y^2+z^2, then n=x^2 +(y+z)^2 + (y-z)^2 237 | if ((n & 3) == 3) { 238 | if (!NTL::IsOdd(sq)) { 239 | sq -= 1; // make sure that sq is odd 240 | p += (sq<<1) + 1; // recover n = sq^2 + p 241 | } // since n, sq are both odd then p must be even 242 | p >>= 1; 243 | while (true) { 244 | if (NTL::ProbPrime(p)) { 245 | auto [y,z] = decomposeProbablePrime(p); 246 | if (!NTL::IsZero(z)) { 247 | // if the above succeeded then n= 248 | ret = {delta, sq, y+z, NTL::abs(y-z)}; 249 | return sortScaled(ret, v); 250 | } 251 | } 252 | sq -= 2; // Next odd sq 253 | if (sq < 0) // Should not happen, no known case 254 | return failed; 255 | p += (sq<<1) + 2; // Recover n = sq^2 + 2p 256 | } 257 | } 258 | 259 | // Case 2: n % 4 = 1 or n % 4 = 2, n = x^2 + p, 260 | // if p prime, p % 4 = 1, p = y^2 + z^2, then n = x^2 + y^2 + z^2 261 | if (!NTL::IsOdd(n - sq)) { // make sure that n-sq is odd 262 | sq -= 1; 263 | p += (sq<<1) + 1; // recover n = sq^2 + p 264 | } // in either case n=1,2 (mod 4), p is odd 265 | while (true) { 266 | if (NTL::ProbPrime(p)) { 267 | auto [y,z] = decomposeProbablePrime(p); 268 | if (!NTL::IsZero(z)) { 269 | ret = {delta,sq,y,z}; 270 | return sortScaled(ret, v); 271 | } 272 | } 273 | sq -= 2; // Next sq 274 | if (sq < 0) // Should not happen, no known case 275 | return failed; 276 | p += (sq + 1)<<2; // Recover n = sq^2 + p 277 | } 278 | } 279 | } // end of namespace UTILS 280 | -------------------------------------------------------------------------------- /src/algebra/ternaryMatrix.cpp: -------------------------------------------------------------------------------- 1 | /* regevProofs.cpp - A 0/+-1 matrix 2 | * 3 | * Copyright (C) 2021, LWE-PVSS 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject 11 | * to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | * OTHER DEALINGS IN THE SOFTWARE. 23 | **/ 24 | #include 25 | #include 26 | #include "algebra.hpp" 27 | //#include "regevEnc.hpp" // brings in NTL compatibility 28 | #include "ternaryMatrix.hpp" 29 | extern "C" { 30 | #include 31 | } 32 | 33 | namespace ALGEBRA { 34 | 35 | // fill with random 0/+-1 values, Pr[0]=1/2, Pr[+-1]=1/4 36 | TernaryMatrix& TernaryMatrix::random() { 37 | size_t nBytes = NumRows() * ((NumCols()+3)/4); 38 | unsigned char bytes[nBytes]; 39 | randombytes_buf(bytes, nBytes); // fill buffer with random bytes 40 | return setFromBytes(bytes, NumRows(), NumCols()); 41 | } 42 | 43 | // Set the value from the give nbits, assumes that sizeof(bits)>=n*ceil(m/4) 44 | // If the given bits are random then Pr[0]=1/2, Pr[+-1]=1/4 45 | TernaryMatrix& TernaryMatrix::setFromBytes(unsigned char* bytes) 46 | { 47 | const size_t bytesInRow = (NumCols()+3)/4; 48 | size_t rowByteIdx = 0; // index in bytes[] 49 | for (auto &r : rows) { 50 | for (size_t i=0; i>1); 54 | unsigned char mask2 = (mask1<<1)| 0x55; //theByte ^ ((theByte & 0x55)<<1); 55 | r.rep[i] = theByte & mask1 & mask2; 56 | } 57 | rowByteIdx += bytesInRow; 58 | } 59 | return *this; 60 | } 61 | 62 | template 63 | void leftVecMult(VecType& result, const VecType& v, const TernaryMatrix& mat) 64 | { 65 | if (v.length() != mat.NumRows()) { 66 | throw std::runtime_error("TernaryMatrix::leftVecMult: v*M dimension mismatch"); 67 | } 68 | resize(result, mat.NumCols()); 69 | for (size_t j=0; j(result, v, mat); 84 | } 85 | void leftVecMult(EVector& result, const EVector& v, const TernaryMatrix& mat) { 86 | leftVecMult(result, v, mat); 87 | } 88 | 89 | template 90 | void rightVecMult(VecType& result, const TernaryMatrix& mat, const VecType& v) 91 | { 92 | if (v.length() != mat.NumCols()) { 93 | throw std::runtime_error("TernaryMatrix::rightVecMult: M*v dimension mismatch"); 94 | } 95 | resize(result, mat.NumRows()); 96 | for (size_t j=0; j(result, mat, v); 111 | } 112 | void rightVecMult(EVector& result, const TernaryMatrix& mat, const EVector& v) { 113 | rightVecMult(result, mat, v); 114 | } 115 | 116 | template 117 | void leftMatMult(MatType& result, const MatType& mat1, const TernaryMatrix& mat2) 118 | { 119 | if (mat1.NumCols() != mat2.NumRows()) { 120 | throw std::runtime_error("TernaryMatrix::leftMatMult: M1*M2 dimension mismatch"); 121 | } 122 | resize(result, mat1.NumRows(), mat2.NumCols()); 123 | for (size_t i=0; i(result, mat1, mat2); 128 | } 129 | void leftMatMult(EMatrix& result, const EMatrix& mat1, const TernaryMatrix& mat2) { 130 | leftMatMult(result, mat1, mat2); 131 | } 132 | 133 | template 134 | void rightMatMult(MatType& result, const TernaryMatrix& mat1, const MatType& mat2) 135 | { 136 | if (mat1.NumCols() != mat2.NumRows()) { 137 | throw std::runtime_error("TernaryMatrix::rightMatMult: M1*M2 dimension mismatch"); 138 | } 139 | resize(result, mat1.NumRows(), mat2.NumCols()); 140 | VecType inTmp, outTmp; 141 | resize(inTmp, mat2.NumRows()); 142 | for (size_t j=0; j(result, mat1, mat2); 152 | } 153 | void rightMatMult(EMatrix& result, const TernaryMatrix& mat1, const EMatrix& mat2) { 154 | rightMatMult(result, mat1, mat2); 155 | } 156 | 157 | 158 | size_t TernaryEMatrix::degree; // e = degree of GF(p^e) 159 | Element TernaryEMatrix::X; // The ring element whose representation is X 160 | void TernaryEMatrix::init() { 161 | TernaryEMatrix::degree = NTL::deg(NTL::ZZ_pE::modulus()); 162 | NTL::ZZ_pX px; 163 | NTL::SetCoeff(px,1); // Set the coeff of X^1 to 1 164 | conv(TernaryEMatrix::X, px); // convert X to an element of GP(p^e) 165 | } 166 | 167 | // Accessing an element as matrix(i,j), note that this returns a "copy" 168 | // of the stored element, not a reference to it. So while x=matrix(i,j) 169 | // works, matrix(i,j) = x does not. 170 | Element TernaryEMatrix::operator()(size_t i, size_t j) { 171 | Element e; 172 | SVector v; 173 | v.SetLength(degree); 174 | for (size_t n=0; n < Ms.size(); n++) { 175 | v[n] = Ms[n][i][j]; 176 | } 177 | conv(e, v); // build from coefficients 178 | return e; 179 | } 180 | 181 | // randomize or set from bytes. Fill with 0/+-1 representated values, 182 | // for every coefficient we have Pr[0]=1/2, Pr[+-1]=1/4 183 | TernaryEMatrix& TernaryEMatrix::random() { 184 | const size_t nBytes = degree * NumRows() * ((NumCols()+3)/4); 185 | unsigned char bytes[nBytes]; 186 | randombytes_buf(bytes, nBytes); // fill buffer with random bytes 187 | return setFromBytes(bytes, NumRows(), NumCols()); 188 | } 189 | 190 | // Set the value from the give nbits, assumes that sizeof(bits)>=ceil(n*m/4) 191 | // If the given bits are random then Pr[0]=1/2, Pr[+-1]=1/4 192 | TernaryEMatrix& TernaryEMatrix::setFromBytes(unsigned char* bytes) { 193 | const size_t bytesPerMat = NumRows() * ((NumCols()+3)/4); 194 | for (size_t i=0; i< Ms.size(); i++) 195 | Ms[i].setFromBytes(bytes + (i*bytesPerMat)); 196 | return *this; 197 | } 198 | 199 | /*EVector& operator+=(EVector& ev, const SVector& sv) { 200 | if (ev.length() != sv.length()) { 201 | throw std::runtime_error("EVector += SVector: size mismatch"); 202 | } 203 | for (size_t i=0; i(result, ev, mat.Ms[mat.degree -1]); 214 | for (int i=mat.degree-2; i>=0; --i) { 215 | EVector tmp; 216 | leftVecMult(tmp, ev, mat.Ms[i]); 217 | result *= mat.X; 218 | result += tmp; 219 | } 220 | } 221 | // result = trenaryMatrix * eVector 222 | void rightVecMult(EVector& result, const TernaryEMatrix& mat, const EVector& ev) { 223 | if (mat.Ms.size() != mat.degree || mat.degree <= 0) { 224 | throw std::runtime_error("TernaryEMatrix::rightVecMult: uninitialized matrix"); 225 | } 226 | rightVecMult(result, mat.Ms[mat.degree -1], ev); 227 | for (int i=mat.degree-2; i>=0; --i) { 228 | EVector tmp; 229 | rightVecMult(tmp, mat.Ms[i], ev); 230 | result *= mat.X; 231 | result += tmp; 232 | } 233 | } 234 | // result = eMatrix * trenaryMatrix 235 | void leftMatMult(EMatrix& result, const EMatrix& mat1, const TernaryEMatrix& mat2) { 236 | if (mat2.Ms.size() != mat2.degree || mat2.degree <= 0) { 237 | throw std::runtime_error("TernaryEMatrix::leftMatMult: uninitialized matrix"); 238 | } 239 | leftMatMult(result, mat1, mat2.Ms[mat2.degree -1]); 240 | for (int i=mat2.degree-2; i>=0; --i) { 241 | EMatrix tmp; 242 | leftMatMult(tmp, mat1, mat2.Ms[i]); 243 | result *= mat2.X; 244 | result += tmp; 245 | } 246 | } 247 | // result = trenaryMatrix * eMatrix 248 | void rightMatMult(EMatrix& result, const TernaryEMatrix& mat1, const EMatrix& mat2) { 249 | if (mat1.Ms.size() != mat1.degree || mat1.degree <= 0) { 250 | throw std::runtime_error("TernaryEMatrix::rightMatMult: uninitialized matrix"); 251 | } 252 | rightMatMult(result, mat1.Ms[mat1.degree -1], mat2); 253 | for (int i=mat1.degree-2; i>=0; --i) { 254 | EMatrix tmp; 255 | rightMatMult(tmp, mat1.Ms[i], mat2); 256 | result *= mat1.X; 257 | result += tmp; 258 | } 259 | } 260 | 261 | } // end of namespace REGEVENC 262 | 263 | -------------------------------------------------------------------------------- /src/dlproofs/naive.cpp: -------------------------------------------------------------------------------- 1 | /* naive.cpp - Naive implementation of multi-exponentiation and related ops 2 | * 3 | * Copyright (C) 2021, LWE-PVSS 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject 11 | * to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | * OTHER DEALINGS IN THE SOFTWARE. 23 | **/ 24 | #include "scalar25519.hpp" 25 | #include "point25519.hpp" 26 | #include "pedersen.hpp" 27 | 28 | namespace DLPROOFS { 29 | using CRV25519::Scalar, CRV25519::Point; 30 | 31 | // compute \sum_i Gi*xi, naive implementation 32 | Point multiExp(const Point* Gs, size_t n, const Scalar* xes) { 33 | Point c = Point::identity(); 34 | for (size_t i=0; i 6 | * 7 | * Derived from keccak-tiny, with attribution note preserved below: 8 | * 9 | * Implementor: David Leon Gil 10 | * License: CC0, attribution kindly requested. Blame taken too, 11 | * but not liability. 12 | */ 13 | #include "libmerlin/merlin.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /******** The Keccak-f[1600] permutation ********/ 22 | 23 | /*** Constants. ***/ 24 | static const uint8_t rho[24] = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 25 | 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44}; 26 | static const uint8_t pi[24] = {10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 27 | 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1}; 28 | static const uint64_t RC[24] = {1ULL, 29 | 0x8082ULL, 30 | 0x800000000000808aULL, 31 | 0x8000000080008000ULL, 32 | 0x808bULL, 33 | 0x80000001ULL, 34 | 0x8000000080008081ULL, 35 | 0x8000000000008009ULL, 36 | 0x8aULL, 37 | 0x88ULL, 38 | 0x80008009ULL, 39 | 0x8000000aULL, 40 | 0x8000808bULL, 41 | 0x800000000000008bULL, 42 | 0x8000000000008089ULL, 43 | 0x8000000000008003ULL, 44 | 0x8000000000008002ULL, 45 | 0x8000000000000080ULL, 46 | 0x800aULL, 47 | 0x800000008000000aULL, 48 | 0x8000000080008081ULL, 49 | 0x8000000000008080ULL, 50 | 0x80000001ULL, 51 | 0x8000000080008008ULL}; 52 | 53 | /*** Helper macros to unroll the permutation. ***/ 54 | #define rol(x, s) (((x) << s) | ((x) >> (64 - s))) 55 | #define REPEAT6(e) e e e e e e 56 | #define REPEAT24(e) REPEAT6(e e e e) 57 | #define REPEAT5(e) e e e e e 58 | #define FOR5(v, s, e) \ 59 | v = 0; \ 60 | REPEAT5(e; v += s;) 61 | 62 | /*** Keccak-f[1600] ***/ 63 | static /*inline*/ void keccakf(void* state) { 64 | uint64_t* a = (uint64_t*)state; 65 | uint64_t b[5] = {0}; 66 | uint64_t t = 0; 67 | uint8_t x, y; 68 | int i; 69 | 70 | for (i = 0; i < 24; i++) { 71 | /* Theta */ 72 | FOR5(x, 1, b[x] = 0; FOR5(y, 5, b[x] ^= a[x + y];)) 73 | FOR5(x, 1, FOR5(y, 5, a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1);)) 74 | /* Rho and pi */ 75 | t = a[1]; 76 | x = 0; 77 | REPEAT24(b[0] = a[pi[x]]; a[pi[x]] = rol(t, rho[x]); t = b[0]; x++;) 78 | /* Chi */ 79 | FOR5(y, 5, 80 | FOR5(x, 1, b[x] = a[y + x];) 81 | FOR5(x, 1, a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);)) 82 | /* Iota */ 83 | a[0] ^= RC[i]; 84 | } 85 | } 86 | 87 | /******** A Strobe-128 context; internal functions. ********/ 88 | 89 | #define STROBE_R 166 90 | 91 | #define FLAG_I (1) 92 | #define FLAG_A (1 << 1) 93 | #define FLAG_C (1 << 2) 94 | #define FLAG_T (1 << 3) 95 | #define FLAG_M (1 << 4) 96 | #define FLAG_K (1 << 5) 97 | 98 | static /*inline*/ void strobe128_run_f(merlin_strobe128* ctx) { 99 | ctx->state_bytes[ctx->pos] ^= ctx->pos_begin; 100 | ctx->state_bytes[ctx->pos + 1] ^= 0x04; 101 | ctx->state_bytes[STROBE_R + 1] ^= 0x80; 102 | keccakf(ctx->state); 103 | ctx->pos = 0; 104 | ctx->pos_begin = 0; 105 | } 106 | 107 | static void strobe128_absorb(merlin_strobe128* ctx, 108 | const uint8_t* data, 109 | size_t data_len) { 110 | size_t i; 111 | for (i = 0; i < data_len; ++i) { 112 | ctx->state_bytes[ctx->pos] ^= data[i]; 113 | ctx->pos += 1; 114 | if (ctx->pos == STROBE_R) { 115 | strobe128_run_f(ctx); 116 | } 117 | } 118 | } 119 | 120 | static void strobe128_overwrite(merlin_strobe128* ctx, 121 | const uint8_t* data, 122 | size_t data_len) { 123 | size_t i; 124 | for (i = 0; i < data_len; ++i) { 125 | ctx->state_bytes[ctx->pos] = data[i]; 126 | ctx->pos += 1; 127 | if (ctx->pos == STROBE_R) { 128 | strobe128_run_f(ctx); 129 | } 130 | } 131 | } 132 | 133 | static void strobe128_squeeze(merlin_strobe128* ctx, uint8_t* data, size_t data_len) { 134 | size_t i; 135 | for (i = 0; i < data_len; ++i) { 136 | data[i] = ctx->state_bytes[ctx->pos]; 137 | ctx->state_bytes[ctx->pos] = 0; 138 | ctx->pos += 1; 139 | if (ctx->pos == STROBE_R) { 140 | strobe128_run_f(ctx); 141 | } 142 | } 143 | } 144 | 145 | static /*inline*/ void strobe128_begin_op(merlin_strobe128* ctx, 146 | uint8_t flags, 147 | uint8_t more) { 148 | if (more) { 149 | /* Changing flags while continuing is illegal */ 150 | assert(ctx->cur_flags == flags); 151 | return; 152 | } 153 | 154 | /* T flag is not supported */ 155 | assert(!(flags & FLAG_T)); 156 | 157 | uint8_t old_begin = ctx->pos_begin; 158 | ctx->pos_begin = ctx->pos + 1; 159 | ctx->cur_flags = flags; 160 | 161 | uint8_t data[2] = {old_begin, flags}; 162 | strobe128_absorb(ctx, data, 2); 163 | 164 | /* Force running the permutation if C or K is set. */ 165 | uint8_t force_f = 0 != (flags & (FLAG_C | FLAG_K)); 166 | 167 | if (force_f && ctx->pos != 0) { 168 | strobe128_run_f(ctx); 169 | } 170 | } 171 | 172 | /******** A Strobe-128 context; external (to Strobe) functions. ********/ 173 | 174 | static void strobe128_meta_ad(merlin_strobe128* ctx, 175 | const uint8_t* data, 176 | size_t data_len, 177 | uint8_t more) { 178 | strobe128_begin_op(ctx, FLAG_C | FLAG_A, more); 179 | strobe128_absorb(ctx, data, data_len); 180 | } 181 | 182 | static void strobe128_ad(merlin_strobe128* ctx, 183 | const uint8_t* data, 184 | size_t data_len, 185 | uint8_t more) { 186 | strobe128_begin_op(ctx, FLAG_A, more); 187 | strobe128_absorb(ctx, data, data_len); 188 | } 189 | 190 | static void strobe128_prf(merlin_strobe128* ctx, 191 | uint8_t* data, 192 | size_t data_len, 193 | uint8_t more) { 194 | strobe128_begin_op(ctx, FLAG_I | FLAG_A | FLAG_C, more); 195 | strobe128_squeeze(ctx, data, data_len); 196 | } 197 | 198 | static void strobe128_key(merlin_strobe128* ctx, 199 | const uint8_t* data, 200 | size_t data_len, 201 | uint8_t more) { 202 | strobe128_begin_op(ctx, FLAG_C | FLAG_A, more); 203 | strobe128_overwrite(ctx, data, data_len); 204 | } 205 | 206 | static void strobe128_init(merlin_strobe128* ctx, 207 | const uint8_t* label, 208 | size_t label_len) { 209 | uint8_t init[18] = {1, 168, 1, 0, 1, 96, 83, 84, 82, 210 | 79, 66, 69, 118, 49, 46, 48, 46, 50}; 211 | memset(ctx->state_bytes, 0, 200); 212 | memcpy(ctx->state_bytes, init, 18); 213 | keccakf(ctx->state); 214 | ctx->pos = 0; 215 | ctx->pos_begin = 0; 216 | ctx->cur_flags = 0; 217 | 218 | strobe128_meta_ad(ctx, label, label_len, 0); 219 | } 220 | 221 | /******** The Merlin transcript functions. ********/ 222 | 223 | void merlin_transcript_init(merlin_transcript* mctx, 224 | const uint8_t* label, 225 | size_t label_len) { 226 | uint8_t merlin_label[] = "Merlin v1.0"; 227 | strobe128_init(&mctx->sctx, merlin_label, 11); 228 | merlin_transcript_commit_bytes(mctx, (uint8_t*)"dom-sep", 7, label, label_len); 229 | } 230 | 231 | void merlin_transcript_commit_bytes(merlin_transcript* mctx, 232 | const uint8_t* label, 233 | size_t label_len, 234 | const uint8_t* message, 235 | size_t message_len) { 236 | /* XXX hack */ 237 | uint64_t message_len_bytes = message_len; 238 | strobe128_meta_ad(&mctx->sctx, label, label_len, 0); 239 | strobe128_meta_ad(&mctx->sctx, (uint8_t*)&message_len_bytes, 4, 1); 240 | strobe128_ad(&mctx->sctx, message, message_len, 0); 241 | } 242 | 243 | void merlin_transcript_challenge_bytes(merlin_transcript* mctx, 244 | const uint8_t* label, 245 | size_t label_len, 246 | uint8_t* buffer, 247 | size_t buffer_len) { 248 | /* XXX hack */ 249 | uint64_t buffer_len_bytes = buffer_len; 250 | strobe128_meta_ad(&mctx->sctx, label, label_len, 0); 251 | strobe128_meta_ad(&mctx->sctx, (uint8_t*)&buffer_len_bytes, 4, 1); 252 | strobe128_prf(&mctx->sctx, buffer, buffer_len, 0); 253 | } 254 | 255 | void merlin_rng_init(merlin_rng* mrng, const merlin_transcript* mctx) { 256 | memcpy(&mrng->sctx, &mctx->sctx, sizeof(merlin_strobe128)); 257 | mrng->finalized = 0; 258 | } 259 | 260 | void merlin_rng_commit_witness_bytes(merlin_rng* mrng, 261 | const uint8_t* label, 262 | size_t label_len, 263 | const uint8_t* witness, 264 | size_t witness_len) { 265 | assert(!mrng->finalized); 266 | /* XXX hack */ 267 | uint64_t witness_len_bytes = witness_len; 268 | strobe128_meta_ad(&mrng->sctx, label, label_len, 0); 269 | strobe128_meta_ad(&mrng->sctx, (uint8_t*)&witness_len_bytes, 4, 1); 270 | strobe128_key(&mrng->sctx, witness, witness_len, 0); 271 | } 272 | 273 | void merlin_rng_finalize(merlin_rng* mrng, const uint8_t entropy[32]) { 274 | assert(!mrng->finalized); 275 | strobe128_meta_ad(&mrng->sctx, (uint8_t*)"rng", 3, 0); 276 | strobe128_key(&mrng->sctx, entropy, 32, 0); 277 | mrng->finalized = 1; 278 | } 279 | 280 | void merlin_rng_random_bytes(merlin_rng* mrng, uint8_t* buffer, size_t buffer_len) { 281 | assert(mrng->finalized); 282 | /* XXX hack */ 283 | uint64_t buffer_len_bytes = buffer_len; 284 | strobe128_meta_ad(&mrng->sctx, (uint8_t*)&buffer_len_bytes, 4, 1); 285 | strobe128_prf(&mrng->sctx, buffer, buffer_len, 0); 286 | } 287 | 288 | void merlin_rng_wipe(merlin_rng* mrng) { 289 | #ifdef HAVE_EXPLICIT_BZERO 290 | explicit_bzero(&mrng->sctx, sizeof(merlin_strobe128)); 291 | #else 292 | memset(&mrng->sctx, 0, sizeof(merlin_strobe128)); 293 | #endif 294 | } 295 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* main.cpp - a "main" file, just a debugging tool 2 | * 3 | * Copyright (C) 2021, LWE-PVSS 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining 6 | * a copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject 11 | * to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | * OTHER DEALINGS IN THE SOFTWARE. 23 | **/ 24 | 25 | // This file is just a convenience, a handy tool that lets us run 26 | // small porgrams without having to use the awkward ctest syntax. 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | using namespace std; 35 | 36 | #include 37 | #include "regevEnc.hpp" 38 | #include "regevProofs.hpp" 39 | 40 | using namespace ALGEBRA; 41 | using namespace REGEVENC; 42 | 43 | int main(int argc, char** argv) { 44 | // std::cout << "- Found GMP version "<<__GNU_MP__ < 1) { 50 | nParties = std::stoi(argv[1]); 51 | } 52 | if (nParties < 32 || nParties > 4096) 53 | nParties = 512; 54 | std::cout << "nParties="< kgNoise(gpk.enn); 76 | std::vector sk(gpk.enn); 77 | std::vector pk(gpk.enn); 78 | auto start = chrono::steady_clock::now(); 79 | crsTicks = 0; 80 | for (int i=0; i(end - start).count(); 87 | std::cout < ptxt1(gpk.enn); 92 | std::vector ctxt1(gpk.enn); 93 | // secret sharing of a random value , the secret itself is sshr[0] 94 | ALGEBRA::SVector sshr; 95 | ssp.randomSharing(sshr); 96 | for (int i=0; i(end - start).count(); 107 | std::cout <(end - start).count(); 119 | std::cout << gpk.tee << " decryptions in "<lagrangeCoeffs(interval(1,gpk.tee+1)); 155 | 156 | crsTicks = 0; 157 | proveDecryption(pd, ctxtMat, ctxtVec, ptxt2, sk[partyIdx], decNoise); 158 | proveEncryption(pd, ctxt2.first, ctxt2.second, ptxt3, encRnd, eNoise.first, eNoise.second); 159 | proveKeyGen(pd, partyIdx, sk[partyIdx], kgNoise[partyIdx]); 160 | proveReShare(pd, lagrange, ptxt2, ptxt3); 161 | proveSmallness(pd); 162 | 163 | end = chrono::steady_clock::now(); 164 | ticks = chrono::duration_cast(end - start).count(); 165 | std::cout << "preparing to prove and committing in "<(end - start).count(); 201 | std::cout << "aggregating constaints in "<(end - start).count(); 213 | std::cout << "proving linear in "<(end - start).count(); 225 | std::cout << "verifying linear in "<(end - start).count(); 242 | std::cout << "proving quadratic in "<(end - start).count(); 256 | std::cout << "verifying quadratic in "< 269 | #include 270 | #include "bulletproof.hpp" 271 | 272 | int main(int, char**) { 273 | constexpr size_t pfSize = 13; 274 | 275 | // build a constraint: sum_i ai*bi = b = \sum_bi^2 276 | DLPROOFS::LinConstraint cnstrL; 277 | for (size_t i=0; i indexes; 295 | for (auto& elem: xes) // elem = {idx:scalar} 296 | indexes.insert(indexes.end(), elem.first); 297 | 298 | auto [normSq, prNS] = DLPROOFS::proveNormSquared("blah", xes); 299 | std::cout << "norm: "< 25 | #include 26 | #include "shamir.hpp" 27 | #include "regevProofs.hpp" 28 | 29 | using namespace ALGEBRA; 30 | 31 | namespace TOOLS { 32 | 33 | // compute the parity-check matrix H 34 | void SharingParams::computeKernel() { 35 | int nHcols = n()+1; // number of columns in H 36 | 37 | // Set the generating matrix G, and then H is its kernel. The columns 38 | // of G span the space of valid sharings at the evaluation points 39 | // (0, i_1, i_2, ..., i_n): The j'th column is the evaluation of 40 | // the polynomial p(X)=X^j at all these points. 41 | SMatrix G; 42 | resize(G, nHcols, t()); 43 | 44 | // The first columns of G is an all-1 column 45 | for (int i=0; i0); 59 | assert(H.NumRows()==nHcols-t() && H.NumCols()==nHcols); 60 | #endif 61 | } 62 | 63 | // Compute random secret-sharings, the secret itself is returned in 64 | // the 1st entry of the vector v, followed by the shares in all the 65 | // evaluation points (in order). 66 | void SharingParams::newSharing(SVector& v, const Scalar& s) const { 67 | // Choose a random polynomial with s as the free term 68 | SPoly p; 69 | for (int i=t()-1; i>0; --i) { 70 | Scalar r; 71 | randomizeScalar(r); 72 | SetCoeff(p, i, r); 73 | } 74 | SetCoeff(p, 0, s); // the free term is s 75 | 76 | // Evaluare p in all the evaluation points 77 | SVector evPoints; 78 | resize(evPoints, n()+1); 79 | int i=1; // start from 1 since evPoints[0] = 0 80 | for (auto ep: evalPoints) // ep is an int evaluation points 81 | conv(evPoints[i++], ep); 82 | 83 | // Multi-evaluation 84 | eval(v, p, evPoints); 85 | }; 86 | 87 | // Compute the lagrange coefficients for a size-t reconstruction set 88 | // (i.e. a t-subset of evalPoints). For a set {x_1, x_2, ..., x_t}, 89 | // this function returns a t-vector of scalars {s_1,s_2, ..., s_t} 90 | // where s_i = \prod_{j\ne i} x_j/(x_j-x_i). 91 | SVector SharingParams::lagrangeCoeffs(const EvalSet& recSet) const { 92 | SVector v; resize(v, recSet.size()); 93 | int i = 0; 94 | for (auto xi: recSet) { // go over the evaluation points 95 | Scalar numer, denom; 96 | conv(numer, 1); // initialize both to 1 97 | conv(denom, 1); // initialize both to 1 98 | for (auto xj: recSet) { // go over the evaluation points 99 | if (xi != xj) { 100 | numer *= xj; 101 | denom *= (xj-xi); 102 | } 103 | } 104 | v[i++] = numer / denom; 105 | } 106 | return v; 107 | } 108 | 109 | // Recover the secret from its sharing at the given reconstruction set 110 | Scalar SharingParams::getSecret(const SVector& sharing, const EvalSet& recSet) { 111 | SVector coeffs = lagrangeCoeffs(recSet); 112 | return innerProduct(coeffs, sharing); 113 | } 114 | 115 | } // end of namespace TOOLS 116 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB files "test_*.cpp") 2 | 3 | foreach(file ${files}) 4 | string(REGEX REPLACE "(^.*/|\\.[^.]*$)" "" file_without_ext ${file}) 5 | # add_executable(${file_without_ext} ${file} ${SOURCES}) 6 | add_executable(${file_without_ext} ${file}) 7 | target_link_libraries(${file_without_ext} ${PROJECT_LIBS}) 8 | add_test(${file_without_ext} ${file_without_ext}) 9 | set_tests_properties(${file_without_ext} 10 | PROPERTIES 11 | PASS_REGULAR_EXPRESSION "Test passed") 12 | set_tests_properties(${file_without_ext} 13 | PROPERTIES 14 | FAIL_REGULAR_EXPRESSION "(Exception|Test failed)") 15 | set_tests_properties(${file_without_ext} 16 | PROPERTIES 17 | TIMEOUT 120) 18 | endforeach() 19 | 20 | -------------------------------------------------------------------------------- /tests/test_algebra.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "utils.hpp" 3 | #include "algebra.hpp" 4 | #include "regevEnc.hpp" 5 | #include "utils.hpp" 6 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 7 | 8 | using namespace ALGEBRA; 9 | 10 | /********** TODO: Things to test: *************** 11 | size_t log2roundUp(const BigInt& n); 12 | BigInt toBigInt(long n); 13 | BigInt scalar2bigInt(const Scalar& s); 14 | void scalarBytes(unsigned char *buf, const Scalar& s, size_t bufSize); 15 | void elementBytes(unsigned char *buf, const Element& e, size_t bufSize); 16 | void scalarFromBytes(Scalar& s, const unsigned char *buf, size_t bufSize); 17 | void elementFromBytes(Element& e, const unsigned char *buf, size_t bufSize); 18 | 19 | Scalar innerProduct(const SVector& v1, const SVector& v2); 20 | Element innerProduct(const EVector& v1, const EVector& v2); 21 | 22 | template 23 | VecType& push_back(VecType &v, const ElemType& s); 24 | 25 | void conv(Element& e, const SVector& v); 26 | void conv(SVector& v, const Element& e); 27 | ************************************************************************/ 28 | 29 | void chooseEvector(EVector& v) { 30 | resize(v, 6); 31 | SVector tmp; 32 | resize(tmp, REGEVENC::GlobalKey::ell); 33 | conv(tmp[0], BigInt(1000)); 34 | conv(tmp[1], BigInt(-50)); 35 | for (int i=0; i500) return false; 51 | if (coeff(lo[i],j) + 1000*coeff(hi[i],j) != coeff(v[i],j)) 52 | return false; 53 | } 54 | } 55 | return true; 56 | } 57 | 58 | bool test_vec2map() { 59 | EVector vE; 60 | SVector vS; 61 | chooseEvector(vE); 62 | resize(vS, vE.length()); 63 | for (int i=0; i indexes; 128 | for (size_t i=0; i 2 | #include 3 | #include 4 | #include "constraints.hpp" 5 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 6 | 7 | using namespace DLPROOFS; 8 | 9 | bool test_splitBy() 10 | { 11 | PtxtVec vec = { 12 | {1, Scalar().setInteger(1)}, 13 | {3, Scalar().setInteger(3)}, 14 | {4, Scalar().setInteger(4)}, 15 | {6, Scalar().setInteger(6)}, 16 | {8, Scalar().setInteger(8)}, 17 | {10, Scalar().setInteger(10)} 18 | }; 19 | std::set idxSet = {2, 3, 4, 5, 7}; 20 | PtxtVec intersection, setDiff; 21 | size_t nextIdx = splitPtxtVec(intersection, setDiff, vec, idxSet); 22 | if (intersection != PtxtVec{{3,Scalar().setInteger(3)},{4,Scalar().setInteger(4)}}) 23 | return false; 24 | if (setDiff!= PtxtVec{{1,Scalar().setInteger(1)},{6, Scalar().setInteger(6)},{8, Scalar().setInteger(8)},{10, Scalar().setInteger(10)}}) 25 | return false; 26 | return (nextIdx == 11); 27 | } 28 | 29 | bool test_innerProduct() { 30 | PtxtVec v1 = { 31 | {1, Scalar().setInteger(2)}, 32 | {3, Scalar().setInteger(3)}, 33 | {4, Scalar().setInteger(5)}, 34 | {6, Scalar().setInteger(7)}, 35 | {8, Scalar().setInteger(11)} 36 | }; 37 | PtxtVec v2 = { 38 | {2, Scalar().setInteger(1)}, 39 | {3, Scalar().setInteger(2)}, 40 | {4, Scalar().setInteger(3)}, 41 | {8, Scalar().setInteger(4)}, 42 | {10, Scalar().setInteger(5)} 43 | }; 44 | return ( innerProduct(v1,v2) == Scalar().setInteger(65) ); 45 | } 46 | 47 | static bool test_Constraints() 48 | { 49 | // merge constraints 50 | std::vector cs(3); 51 | cs[0].addTerm(1, Scalar().setInteger(1) 52 | ).addTerm(2, Scalar().setInteger(-2) 53 | ).addTerm(3, Scalar().setInteger(3)); 54 | cs[0].equalsTo.setInteger(2); 55 | 56 | cs[1].addTerm(2, Scalar().setInteger(1) 57 | ).addTerm(3, Scalar().setInteger(2) 58 | ).addTerm(5, Scalar().setInteger(-3)); 59 | cs[1].equalsTo.setInteger(3); 60 | 61 | cs[2].addTerm(3, Scalar().setInteger(1) 62 | ).addTerm(4, Scalar().setInteger(2) 63 | ).addTerm(5, Scalar().setInteger(3)); 64 | cs[2].equalsTo.setInteger(1); 65 | 66 | std::vector coefs(3); 67 | coefs[0].setInteger(1); 68 | coefs[1].setInteger(2); 69 | coefs[2].setInteger(3); 70 | 71 | LinConstraint c1, c2; 72 | 73 | c1.merge(cs, coefs); 74 | // This supposed to yeild {[1->1, 3->10, 4->6, 5->3], 11} 75 | 76 | c2.addTerm(1, Scalar().setInteger(1) 77 | ).addTerm(4, Scalar().setInteger(6) 78 | ).addTerm(3, Scalar().setInteger(4) 79 | ).addTerm(5, Scalar().setInteger(3) 80 | ).addTerm(3, Scalar().setInteger(6)); // added both 3->4 nad 3->6 81 | c2.equalsTo.setInteger(11); 82 | if (c1 != c2) 83 | return false; 84 | 85 | PtxtVec xs{ // satisfying the contraint c1 86 | {1,Scalar().setInteger(1)}, 87 | {3,Scalar().setInteger(1)}, 88 | {4,Scalar().setInteger(1)}, 89 | {5,Scalar().setInteger(-2)} 90 | }; 91 | if (!checkConstraint(c1, xs)) 92 | return false; 93 | 94 | PtxtVec ys{ // not satisfying the contraint c1 95 | {1,Scalar().setInteger(2)}, 96 | {3,Scalar().setInteger(1)}, 97 | {4,Scalar().setInteger(2)}, 98 | {5,Scalar().setInteger(2)} 99 | }; 100 | if (checkConstraint(c1, ys)) 101 | return false; 102 | 103 | PtxtVec zs{ // index mismatch 104 | {1,Scalar().setInteger(1)}, 105 | {3,Scalar().setInteger(1)}, 106 | {4,Scalar().setInteger(1)}, 107 | {5,Scalar().setInteger(-2)}, 108 | {6,Scalar().setInteger(-2)} 109 | }; 110 | if (checkConstraint(c1, zs)) 111 | return false; 112 | 113 | // check also the loose variant 114 | if (!checkConstraintLoose(c1, zs)) 115 | return false; 116 | zs.erase(5); 117 | if (checkConstraintLoose(c1, zs)) 118 | return false; 119 | 120 | QuadConstraint q1; 121 | q1.addIdx(2).addIdx(3).addIdx(5).addIdx(8); 122 | bool thrown = false; 123 | try { // this should throw 124 | q1.addIdx(3); 125 | } catch (std::runtime_error e) { 126 | thrown = true; 127 | } 128 | if (!thrown) return false; 129 | q1.equalsTo.setInteger(660); 130 | // q1 = {{2, 3, 5, 8}, 660} 131 | 132 | // set c1 = {[2->1, 4->2, 5->3, 6->4, 7->5, 8->6], 8} 133 | c1.terms.clear(); 134 | c1.addTerm(2, Scalar().setInteger(1) 135 | ).addTerm(4, Scalar().setInteger(2) 136 | ).addTerm(5, Scalar().setInteger(3) 137 | ).addTerm(6, Scalar().setInteger(4) 138 | ).addTerm(7, Scalar().setInteger(5) 139 | ).addTerm(8, Scalar().setInteger(7)); 140 | c1.equalsTo.setInteger(8); 141 | 142 | makeAlmostDisjoint(c1, q1, Scalar().setInteger(5)); 143 | // Supposed to remove indexes 2,5,8 from c1, add index 9 (9->1) to both, 144 | // and set c1.equaltsTo=0, q1.equalsTo=660-5*8=620. So we should have 145 | // c1 = {[4->2, 6->4, 7->5, 9->1], 0} 146 | // q1 = {{2, 3, 5, 8, 9}, 620} 147 | 148 | c2.terms.clear(); 149 | c2.addTerm(4, Scalar().setInteger(2) 150 | ).addTerm(6, Scalar().setInteger(4) 151 | ).addTerm(7, Scalar().setInteger(5) 152 | ).addTerm(9, Scalar().setInteger(1)); 153 | c2.equalsTo.setInteger(0); 154 | if (c1 != c2) { 155 | std::cout << "c1="; c1.debugPrint(); 156 | return false; 157 | } 158 | QuadConstraint q2; 159 | q2.addIdx(2).addIdx(3).addIdx(5).addIdx(8).addIdx(9); 160 | q2.equalsTo.setInteger(620); 161 | if (q1 != q2) { 162 | std::cout << "q1="; q1.debugPrint(); 163 | return false; 164 | } 165 | 166 | q1.indexes.clear(); 167 | q1.addIdx(4).addIdx(1).addIdx(3).addIdx(5); 168 | q1.equalsTo.setInteger(1); // = <(1,1,1,-2),(2,1,2,2)> = 1 169 | 170 | if (!checkConstraint(q1, xs, ys)) 171 | return false; 172 | if (checkConstraint(q1, zs, ys)) 173 | return false; 174 | q1.equalsTo.setInteger(2); 175 | if (checkConstraint(q1, xs, ys)) 176 | return false; 177 | 178 | // Check also the loose variants 179 | q1.equalsTo.setInteger(1); 180 | xs[2] = Scalar().setInteger(2); 181 | if (!checkConstraintLoose(q1, xs, ys)) 182 | return false; 183 | ys.erase(1); 184 | if (checkConstraintLoose(q1, xs, ys)) 185 | return false; 186 | 187 | return true; 188 | } 189 | 190 | int main(int, char**) { 191 | if (!test_Constraints() || !test_splitBy()) 192 | std::cout << LWEVSS_TESTS::failed << std::endl; 193 | else 194 | std::cout << LWEVSS_TESTS::passed << std::endl; 195 | } 196 | -------------------------------------------------------------------------------- /tests/test_foursquares.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utils.hpp" 4 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 5 | 6 | using namespace ALGEBRA; 7 | 8 | bool test_decomposeProbablePrime() 9 | { 10 | auto v = decomposeProbablePrime(NTL::to_ZZ(37)); // 37-1 is a square 11 | if ((sqr(v[0]) + sqr(v[1])) != 37) 12 | return false; 13 | 14 | v = decomposeProbablePrime(NTL::to_ZZ(12049)); // 12049-1 is not a square 15 | if ((sqr(v[0]) + sqr(v[1])) != 12049) 16 | return false; 17 | 18 | // a ~350-bit prime 19 | NTL::ZZ big = NTL::conv("24684249032065892333066123534168930441269525239006410135714283699648991959894332868446109170827166448301044689"); 20 | 21 | v = decomposeProbablePrime(big); 22 | if ((sqr(v[0]) + sqr(v[1])) != big) 23 | return false; 24 | 25 | bool thrown = false; 26 | try { 27 | v = decomposeProbablePrime(NTL::to_ZZ(11)); // =3 mod 4 28 | } catch (std::runtime_error e) { 29 | // std::cout << e.what() << std::endl; 30 | thrown = true; 31 | } 32 | return thrown; 33 | } 34 | 35 | bool test_decompose4() 36 | { 37 | auto v = decompose4(NTL::to_ZZ(100)); // 100 is a square 38 | if ((sqr(v[0])+sqr(v[1])+sqr(v[2])+sqr(v[3])) != 100) 39 | return false; 40 | 41 | v = decompose4(NTL::to_ZZ(11002)); // not a square, =2 mod 4 42 | if ((sqr(v[0])+sqr(v[1])+sqr(v[2])+sqr(v[3])) != 11002) 43 | return false; 44 | 45 | v = decompose4(NTL::to_ZZ(37)); // 37 is a prime, =1 mod 4 46 | if ((sqr(v[0])+sqr(v[1])+sqr(v[2])+sqr(v[3])) != 37) 47 | return false; 48 | 49 | // 2986*64 = 191,104 is an exception case 50 | v = decompose4(NTL::to_ZZ(191104)); 51 | if ((sqr(v[0])+sqr(v[1])+sqr(v[2])+sqr(v[3])) != 191104) 52 | return false; 53 | 54 | // try twenty random large numbers 55 | for (int i=0; i<20; i++) { 56 | NTL::ZZ n = NTL::RandomBits_ZZ(200); 57 | v = decompose4(n); 58 | if ((sqr(v[0])+sqr(v[1])+sqr(v[2])+sqr(v[3])) != n) 59 | return false; 60 | } 61 | return true; 62 | } 63 | 64 | int main(int, char**) { 65 | if (test_decomposeProbablePrime() && test_decompose4()) 66 | std::cout << LWEVSS_TESTS::passed << std::endl; 67 | else 68 | std::cout << LWEVSS_TESTS::failed << std::endl; 69 | return 0; 70 | } 71 | 72 | 73 | #if 0 74 | # 'l' is a list of tuples where the first elements form the argument to 'func' 75 | # and the last element is the expected result. Returns True iff all tests 76 | # succeed. 77 | def testfunc(func, l): 78 | return False not in [func(*x[:-1]) == x[-1] for x in l] 79 | 80 | # Test the gcd function 81 | # Returns True if no error has been detected 82 | def testgcd(): 83 | return testfunc(gcd, [(4, 6, 2), (6, 4, 2), 84 | (1, 1, 1), (2, 1, 1), (1, 2, 1), (13000, 17000, 1000), (3, 4, 1)]) 85 | 86 | # Test the jacobi function by comparing with a pre-computed table 87 | # Returns True if no error has been detected 88 | def testjacobi(): 89 | return testfunc(jacobi, [ 90 | (1, 1, 1), (1, 3, 1), (1, 5, 1), (1, 7, 1), (1, 9, 1), (1, 11, 1), 91 | (1, 13, 1), (1, 15, 1), (1, 17, 1), (1, 19, 1), (1, 21, 1), (1, 23, 92 | 1), (1, 25, 1), (1, 27, 1), (1, 29, 1), (1, 31, 1), (2, 1, 1), (2, 3, 93 | -1), (2, 5, -1), (2, 7, 1), (2, 9, 1), (2, 11, -1), (2, 13, -1), (2, 94 | 15, 1), (2, 17, 1), (2, 19, -1), (2, 21, -1), (2, 23, 1), (2, 25, 1), 95 | (2, 27, -1), (2, 29, -1), (2, 31, 1), (3, 1, 1), (3, 3, 0), (3, 5, 96 | -1), (3, 7, -1), (3, 9, 0), (3, 11, 1), (3, 13, 1), (3, 15, 0), (3, 97 | 17, -1), (3, 19, -1), (3, 21, 0), (3, 23, 1), (3, 25, 1), (3, 27, 0), 98 | (3, 29, -1), (3, 31, -1), (4, 1, 1), (4, 3, 1), (4, 5, 1), (4, 7, 1), 99 | (4, 9, 1), (4, 11, 1), (4, 13, 1), (4, 15, 1), (4, 17, 1), (4, 19, 1), 100 | (4, 21, 1), (4, 23, 1), (4, 25, 1), (4, 27, 1), (4, 29, 1), (4, 31, 101 | 1), (5, 1, 1), (5, 3, -1), (5, 5, 0), (5, 7, -1), (5, 9, 1), (5, 11, 102 | 1), (5, 13, -1), (5, 15, 0), (5, 17, -1), (5, 19, 1), (5, 21, 1), (5, 103 | 23, -1), (5, 25, 0), (5, 27, -1), (5, 29, 1), (5, 31, 1), (6, 1, 1), 104 | (6, 3, 0), (6, 5, 1), (6, 7, -1), (6, 9, 0), (6, 11, -1), (6, 13, -1), 105 | (6, 15, 0), (6, 17, -1), (6, 19, 1), (6, 21, 0), (6, 23, 1), (6, 25, 106 | 1), (6, 27, 0), (6, 29, 1), (6, 31, -1), (7, 1, 1), (7, 3, 1), (7, 5, 107 | -1), (7, 7, 0), (7, 9, 1), (7, 11, -1), (7, 13, -1), (7, 15, -1), (7, 108 | 17, -1), (7, 19, 1), (7, 21, 0), (7, 23, -1), (7, 25, 1), (7, 27, 1), 109 | (7, 29, 1), (7, 31, 1), (8, 1, 1), (8, 3, -1), (8, 5, -1), (8, 7, 1), 110 | (8, 9, 1), (8, 11, -1), (8, 13, -1), (8, 15, 1), (8, 17, 1), (8, 19, 111 | -1), (8, 21, -1), (8, 23, 1), (8, 25, 1), (8, 27, -1), (8, 29, -1), 112 | (8, 31, 1), (9, 1, 1), (9, 3, 0), (9, 5, 1), (9, 7, 1), (9, 9, 0), (9, 113 | 11, 1), (9, 13, 1), (9, 15, 0), (9, 17, 1), (9, 19, 1), (9, 21, 0), 114 | (9, 23, 1), (9, 25, 1), (9, 27, 0), (9, 29, 1), (9, 31, 1), (10, 1, 115 | 1), (10, 3, 1), (10, 5, 0), (10, 7, -1), (10, 9, 1), (10, 11, -1), 116 | (10, 13, 1), (10, 15, 0), (10, 17, -1), (10, 19, -1), (10, 21, -1), 117 | (10, 23, -1), (10, 25, 0), (10, 27, 1), (10, 29, -1), (10, 31, 1), 118 | (11, 1, 1), (11, 3, -1), (11, 5, 1), (11, 7, 1), (11, 9, 1), (11, 11, 119 | 0), (11, 13, -1), (11, 15, -1), (11, 17, -1), (11, 19, 1), (11, 21, 120 | -1), (11, 23, -1), (11, 25, 1), (11, 27, -1), (11, 29, -1), (11, 31, 121 | -1), (12, 1, 1), (12, 3, 0), (12, 5, -1), (12, 7, -1), (12, 9, 0), 122 | (12, 11, 1), (12, 13, 1), (12, 15, 0), (12, 17, -1), (12, 19, -1), 123 | (12, 21, 0), (12, 23, 1), (12, 25, 1), (12, 27, 0), (12, 29, -1), (12, 124 | 31, -1), (13, 1, 1), (13, 3, 1), (13, 5, -1), (13, 7, -1), (13, 9, 1), 125 | (13, 11, -1), (13, 13, 0), (13, 15, -1), (13, 17, 1), (13, 19, -1), 126 | (13, 21, -1), (13, 23, 1), (13, 25, 1), (13, 27, 1), (13, 29, 1), (13, 127 | 31, -1), (14, 1, 1), (14, 3, -1), (14, 5, 1), (14, 7, 0), (14, 9, 1), 128 | (14, 11, 1), (14, 13, 1), (14, 15, -1), (14, 17, -1), (14, 19, -1), 129 | (14, 21, 0), (14, 23, -1), (14, 25, 1), (14, 27, -1), (14, 29, -1), 130 | (14, 31, 1), (15, 1, 1), (15, 3, 0), (15, 5, 0), (15, 7, 1), (15, 9, 131 | 0), (15, 11, 1), (15, 13, -1), (15, 15, 0), (15, 17, 1), (15, 19, -1), 132 | (15, 21, 0), (15, 23, -1), (15, 25, 0), (15, 27, 0), (15, 29, -1), 133 | (15, 31, -1)]) 134 | 135 | # Test the probable prime detector 'func' on all odd numbers between 136 | # n and upperN. Return False iff there is a case where a prime number 137 | # is erroneously flagged as composite. 138 | def testisProbablePrimeFunc(func, n, upperN): 139 | if n & 1 == 0: 140 | n += 1 # Make sure that n is odd 141 | while n <= upperN: 142 | if func(n) < isPrime(n): # Error if composite was prime after all 143 | return False 144 | n += 2 # Next odd 145 | return True 146 | 147 | def testdp(): 148 | return testfunc(dp, [(100, 4, 1, 101), (101, 4, 1, 101), (100, 6, 4, 101), 149 | (101, 6, 4, 101)]) 150 | 151 | def testisnpk(): 152 | return testfunc(isnpk, [(4, 13, True), (2, 5003, True), (4, 5003, False), 153 | (2, (1 << 31) - 1, False), (46, (1 << 31) - 1, True), (32, 3, True), 154 | (20, 5, True), (22, 5, False)]) 155 | 156 | # Test the iunit function for arguments between p and upperP making sure 157 | # that the argument is a (probable) prime congruent 1 modulo 4 158 | # Returns True if no error has been detected 159 | def testiunit(p, upperP): 160 | p = ((p >> 2) << 2) + 1 # Make sure that p % 4 = 1 161 | upperP = min(upperP, 42799) # and that upperP does not exceed range where 162 | while p <= upperP: # isProbablePrime works correctly. 163 | if isProbablePrime(p): 164 | if (iunit(p)[0] ** 2 + 1) % p: # This is the check 165 | return False 166 | p += 4 # maintain invariant p % 4 = 1 167 | return True 168 | 169 | # Perform n tests starting at the smallest prime p >= start with p % 4 = 1 170 | def testdecomposeProbablePrime(start, n): 171 | p = ((start >> 2) << 2) + 1 172 | if p < start: 173 | p += 4 174 | while n: 175 | while not isProbablePrime(p): 176 | p += 4 177 | a, b, c = decomposeProbablePrime(p) 178 | if c and (a * a + b * b != p): 179 | return False 180 | n -= 1 181 | p += 4 182 | return True 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /tests/test_merlin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "merlin.hpp" 3 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 4 | 5 | using CRV25519::Scalar, CRV25519::Point, Merlin::MerlinBPctx; 6 | 7 | static bool test() { 8 | Scalar r1 = CRV25519::randomScalar(); 9 | Scalar one = Scalar().setInteger(1); 10 | Scalar ten = Scalar().setInteger(10); 11 | Point rp = CRV25519::randomPoint(); 12 | 13 | DLPROOFS::LinConstraint cnstr; 14 | cnstr.addTerm(1, one); 15 | cnstr.addTerm(4, ten); 16 | cnstr.addTerm(9, r1+one); 17 | cnstr.equalsTo.setInteger(100); 18 | 19 | // generate twice, test that you get the same challenges, 20 | // but different blinding factors 21 | Scalar x1; 22 | Point p1; 23 | std::pair sPair; 24 | { 25 | MerlinBPctx mer("blah"); 26 | mer.processScalar("one", one); 27 | mer.processPoint("rp", rp); 28 | p1 = mer.newGenerator("p1"); 29 | mer.processPoint("p1", p1); 30 | mer.processConstraint("cnstr", cnstr); 31 | sPair = mer.newBlindingFactors("pair", 1, &x1); 32 | x1 = mer.newChallenge("x1"); 33 | } 34 | { 35 | MerlinBPctx mer("blah"); 36 | mer.processScalar("one", one); 37 | mer.processPoint("rp", rp); 38 | if (mer.newGenerator("p1") != p1) 39 | return false; 40 | mer.processPoint("p1", p1); 41 | mer.processConstraint("cnstr", cnstr); 42 | if (mer.newBlindingFactors("pair", 1, &x1) == sPair) 43 | return false; 44 | if (mer.newChallenge("x1") != x1) 45 | return false; 46 | } 47 | return true; 48 | } 49 | int main(int, char**) { 50 | if (!test()) 51 | std::cout << LWEVSS_TESTS::failed << std::endl; 52 | else 53 | std::cout << LWEVSS_TESTS::passed << std::endl; 54 | } 55 | -------------------------------------------------------------------------------- /tests/test_pedersen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pedersen.hpp" 3 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 4 | 5 | using namespace DLPROOFS; 6 | using CRV25519::Scalar, CRV25519::Point; 7 | 8 | bool testUtils() { 9 | if (next2power(0) != 0 || next2power(1) != 1 || next2power(2) != 2 10 | || next2power(3) != 4 || next2power(4) != 4 || next2power(17) != 32 11 | || next2power(0x7edcba9876543210) != 0x8000000000000000) 12 | return false; 13 | //std::cout << "next2power passed\n"; 14 | 15 | if (log2roundUp(1) != 0 || log2roundUp(2) != 1 || log2roundUp(3) != 2 16 | || log2roundUp(127) != 7 || log2roundUp(128) != 7 || log2roundUp(129) != 8) 17 | return false; 18 | //std::cout << "log2roundUp passed\n"; 19 | return true; 20 | } 21 | 22 | bool testMultiExp() { 23 | constexpr size_t nGs = 5; 24 | std::vector Gs(nGs, Point::base()); // a vector of generators 25 | for (size_t i=0; i exps(nGs); // a vector of random scalars 28 | for (auto& x : exps) x.randomize(); 29 | // compute \sum_i G[i] * exps[i]; 30 | Point res = multiExp(Gs.data(), nGs, exps.data()); 31 | 32 | // check the result 33 | Scalar theExp; // compute the overall exponent 34 | for (size_t i=0; i Gs(nGs, Point::base()); // a vector of generators 44 | for (size_t i=0; i Gs(nGs, Point::base()); // a vector of generators 67 | for (size_t i=0; i exps(3); // random scalars (3=log(n) rounded up) 91 | for (auto& x : exps) x.randomize(); 92 | std::vector prods(8); // to store the subset products 93 | prods[0] = Scalar().setInteger(1); 94 | prods[1] = exps[2]; 95 | prods[2] = exps[1]; 96 | prods[3] = exps[1] * exps[2]; 97 | prods[4] = exps[0]; 98 | prods[5] = exps[0] * exps[2]; 99 | prods[6] = exps[0] * exps[1]; 100 | prods[7] = exps[0] * exps[1] * exps[2]; 101 | 102 | std::vector prods8(8); 103 | subsetProduct(prods8.data(), 6, exps.data(), 3, prods[0]); 104 | for (int i=0; i<6; i++) { 105 | if (prods8[i] != prods[i]) 106 | return false; 107 | } 108 | subsetProduct(prods8.data(), 8, exps.data(), 3, prods[0]); 109 | for (int i=0; i<8; i++) { 110 | if (prods8[i] != prods[i]) 111 | return false; 112 | } 113 | 114 | // check for both length-8 and length-6 vectors 115 | std::vector Gs(8, Point::base()); // a vector of generators 116 | for (size_t i=0; i offsets(8); 126 | for (size_t i=0; i<8; i++) offsets[i].setInteger(i); 127 | 128 | Point p6 = expSubsetProduct(Gs.data(), 6, exps.data(), r, 129 | offsets.data()); 130 | if (p6 != multiExp(Gs.data(),6,prods.data())*r +multiExp(Gs.data(),6,offsets.data())) 131 | return false; 132 | 133 | Point p8 = expSubsetProduct(Gs.data(), 8, exps.data(), r, offsets.data()); 134 | if (p8 != multiExp(Gs.data(),8,prods.data())*r +multiExp(Gs.data(),8,offsets.data())) 135 | return false; 136 | 137 | return true; 138 | } 139 | /* 140 | Point commit(const Point* Gs, const Scalar* xes, size_t n, const Scalar& r, 141 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()); 142 | bool verifyCom(const Point& c, const Point* Gs, const Scalar* xes, size_t n, 143 | const Scalar& r, const Point& F=Point::identity(), const Scalar& alpha=Scalar()); 144 | Point commit2(const Point* Gs, const Scalar* xes, 145 | const Point* Hs, const Scalar* ys, size_t n, const Scalar& r, 146 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()); 147 | bool verifyCom2(const Point& c, const Point* Gs, const Scalar* xes, 148 | const Point* Hs, const Scalar* ys, size_t n, const Scalar& r, 149 | const Point& F=Point::identity(), const Scalar& alpha=Scalar()); 150 | 151 | const Point PedersenContext::getG(int i); 152 | const Point PedersenContext::getH(int i); 153 | */ 154 | 155 | int main(int, char**) { 156 | if (!testUtils() || !testMultiExp() || !testFoldGen()) 157 | std::cout << LWEVSS_TESTS::failed << std::endl; 158 | else 159 | std::cout << LWEVSS_TESTS::passed << std::endl; 160 | } 161 | -------------------------------------------------------------------------------- /tests/test_point25519.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "point25519.hpp" 4 | #include "scalar25519.hpp" 5 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 6 | 7 | using namespace CRV25519; 8 | bool test_Point() { 9 | auto& basePoint = Point::base(); 10 | auto& identityPoint = Point::identity(); 11 | 12 | if (!basePoint.isValid()) 13 | return false; 14 | /** Note: identityPoint.isValid() returns flase, since this is the zero element 15 | if (!identityPoint.isValid()) 16 | return false; 17 | */ 18 | Point p; 19 | if (p != Point::identity()) 20 | return false; 21 | 22 | auto twoBase = basePoint + basePoint; 23 | auto two = Scalar().setInteger(2); 24 | 25 | if (twoBase != basePoint*two || twoBase != two*basePoint) 26 | return false; 27 | 28 | auto r = randomPoint(); 29 | if (r-r != identityPoint) //Point myIdentity; 30 | return false; 31 | 32 | unsigned char buf[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 33 | auto r2 = hashToCurve(buf, sizeof buf); 34 | if (!r2.isValid()) 35 | return false; 36 | 37 | auto s = randomScalar(); 38 | auto r3 = basePoint * s; 39 | auto r4 = baseTimesScalar(s); 40 | if (!r3.isValid() || !r4.isValid() || r3 != r4) 41 | return false; 42 | 43 | // check r4 * s = base * s^2 44 | auto r5 = r4 * s; 45 | auto r6 = basePoint * (s*s); 46 | if (!r5.isValid() || !r6.isValid() || r5 != r6) 47 | return false; 48 | 49 | return true; 50 | } 51 | 52 | int main(int, char**) { 53 | if (!test_Point()) 54 | std::cout << LWEVSS_TESTS::failed << std::endl; 55 | else 56 | std::cout << LWEVSS_TESTS::passed << std::endl; 57 | } 58 | -------------------------------------------------------------------------------- /tests/test_regevEnc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 5 | #include "regevEnc.hpp" 6 | 7 | using namespace REGEVENC; 8 | using namespace std; 9 | 10 | #if 0 11 | // Check that indeed pk = sk * A + noise, and |noise|_{infty} <2^{sigma} 12 | static bool verifyKeyPair(Matrix& crs, Matrix& sk, Matrix& noise, Matrix& pk) { 13 | if (pk != sk * crs + noise) 14 | return false; 15 | 16 | BigInt noiseBound = NTL::to_ZZ(1UL) << REGEVENC::sigma; 17 | for (size_t i=0; i(noise[i][j]); 19 | if (2*ezz >= GlobalKey::P()) // map ezz to [-P/2, p/2) 20 | ezz -= GlobalKey::P(); 21 | if (ezz < 0) // compute abs(e) 22 | ezz = -ezz; 23 | if (ezz >= noiseBound) { 24 | std::cout << "|noise|_{infty} not bounded by 2^{sigma}"; 25 | return false; 26 | } 27 | } 28 | return true; 29 | } 30 | #endif 31 | 32 | static bool test_decode() { 33 | // ALGEBRA::Scalar decodePtxt(ALGEBRA::Element& noisyPtxt, 34 | // ALGEBRA::Element* noise=nullptr) const; 35 | return true; 36 | } 37 | 38 | bool test_params() 39 | { 40 | KeyParams kp(256); 41 | return (kp.n==256 && kp.k==2944 && kp.sigmaEnc1==97 && kp.sigmaEnc2==116); 42 | } 43 | 44 | static bool test_Regev() { 45 | KeyParams kp; 46 | kp.k=64; kp.n=64; 47 | kp.sigmaEnc1=10; kp.sigmaEnc2=20; 48 | GlobalKey gpk("testContext",kp); 49 | ALGEBRA::EVector noise1; 50 | auto [sk1,pk1] = gpk.genKeys(&noise1); 51 | auto [sk2,pk2] = gpk.genKeys(); 52 | size_t i1 = gpk.addPK(pk1); 53 | size_t i2 = gpk.addPK(pk2); 54 | for (size_t i=2; i 2 | #include 3 | #include "scalar25519.hpp" 4 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 5 | 6 | using namespace CRV25519; 7 | 8 | static bool testMod() { 9 | auto x = Scalar().setInteger(100); 10 | auto y = Scalar().setInteger(7); 11 | x %= y; // should be 2 12 | if (x != Scalar().setInteger(2)) { 13 | std::cout << "  100 % 7 should be = 2\n"; 14 | return false; 15 | } 16 | 17 | x.setInteger(40); 18 | x %= y; 19 | if (x != Scalar().setInteger(-2)) { 20 | std::cout << "  40 % 7 should be = -2\n"; 21 | return false; 22 | } 23 | 24 | x.setInteger(-100); 25 | x %= y; 26 | if (x != Scalar().setInteger(-2)) { 27 | std::cout << "  -100 % 7 should be = -2\n"; 28 | return false; 29 | } 30 | 31 | x.setInteger(100); 32 | y.setInteger(-7); 33 | x %= y; 34 | if (x != Scalar().setInteger(2)) { 35 | std::cout << "  100 % -7 should be = 2\n"; 36 | return false; 37 | } 38 | 39 | x.setInteger(-100); 40 | x %= y; 41 | if (x != Scalar().setInteger(-2)) { 42 | std::cout << "  -100 % -7 should be = -2\n"; 43 | return false; 44 | } 45 | return true; 46 | } 47 | 48 | static bool test_Scalar() { 49 | Scalar zero = Scalar(); 50 | Scalar one = Scalar().setInteger(1); 51 | Scalar two = one + one; 52 | if (two.bytes[0] != 2) 53 | return false; 54 | for (int i=1; i 2 | #include "shamir.hpp" 3 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 4 | 5 | using namespace TOOLS; 6 | using namespace ALGEBRA; 7 | 8 | // test computeKernel() - computing the parity-check matrix H 9 | bool testKernel() { 10 | EvalSet es; 11 | for (int i=1; i<6; i++) es.insert(i); 12 | SharingParams params(es, 2); 13 | 14 | // a random degree-1 polynomial 15 | Scalar p0, p1; 16 | randomizeScalar(p0); randomizeScalar(p1); 17 | SPoly p; 18 | SetCoeff(p, 0, p1); 19 | SetCoeff(p, 0, p0); 20 | 21 | // evaluate at 0,1,...,5 22 | SVector shares; 23 | resize(shares, 6); 24 | for (int i=0; i<6; i++) { 25 | Scalar s; 26 | conv(s, i); 27 | shares[i] = eval(p, s); 28 | } 29 | 30 | // check that H * shares = 0 31 | SVector zero; 32 | resize(zero, params.H.NumRows()); 33 | return (params.H * shares == zero); 34 | } 35 | 36 | bool testSharing() { 37 | // 2-of-5 sharing 38 | EvalSet es{1,2,3,4,5}; 39 | SharingParams params(es, 2); 40 | 41 | // share a random scalar 42 | SVector shares; 43 | params.randomSharing(shares); 44 | 45 | // reconstruct the secret from shares 2,4 46 | EvalSet recSet{2,4}; 47 | SVector sh2; resize(sh2, 2); 48 | sh2[0] = shares[2]; 49 | sh2[1] = shares[4]; 50 | return params.getSecret(sh2, recSet) == shares[0]; 51 | } 52 | 53 | /*********************** TODO: Things to test: ********************* 54 | SharingParams(const EvalSet& ev, int t); 55 | void newSharing(SVector& v, const Scalar& s) const; 56 | SVector lagrangeCoeffs(const EvalSet& recSet) const; 57 | Scalar getSecret(const SVector& sharing, const EvalSet& recSet); 58 | *******************************************************************/ 59 | 60 | int main(int, char**) { 61 | NTL::ZZ_p::init(NTL::to_ZZ(17)); 62 | if (testKernel() && testSharing()) 63 | std::cout << LWEVSS_TESTS::passed << std::endl; 64 | else 65 | std::cout << LWEVSS_TESTS::failed << std::endl; 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /tests/test_ternaryMatrix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ternaryMatrix.hpp" 4 | #include "tests.hpp" // define strings LWEVSS_TESTS::passed and LWEVSS_TESTS::failed 5 | 6 | using namespace ALGEBRA; 7 | bool basicTests() { 8 | // {12,f7,59,f6} = [00 01 00 10 11 11 01 11 01 01 10 01 11 11 01 10] 9 | // The bits that are actually stored should be 10 | // {12,04,59,06} = [00 01 00 10 00 00 01 00 01 01 10 01 00 00 01 10] 11 | // yealds, e.g., 4-by-4 matrix [-1,0,1,0][0,1,0,0][1,-1,1,1][-1,1,0,0] 12 | // or a 2-by-5 matrix [0,1,0,-1,0][1,1,-1,1,-1] 13 | unsigned char buf[] = {0x12, 0xf7, 0x59, 0xf6}; 14 | unsigned char rep[] = {0x12, 0x04, 0x59, 0x06}; 15 | int res1[4][4] = {{-1,0,1,0},{0,1,0,0},{1,-1,1,1},{-1,1,0,0}}; 16 | int res2[2][5] = {{-1,0,1,0,0},{1,-1,1,1,-1}}; 17 | TernaryMatrix M; 18 | M.setFromBytes(buf,4,4); 19 | // check internal storage 20 | for (size_t i=0; i<4; i++) { 21 | if (M.rows[i].rep[0] != rep[i]) { 22 | printf("M[4,4], input byte %x, expected %x but found %x\n", 23 | buf[i], rep[i], M.rows[i].rep[0]); 24 | return false; 25 | } 26 | } 27 | // check matrix content 28 | if (M.NumRows()!=4 || M.NumCols()!=4) { 29 | printf("expected 4x4 matrix but found %dx%d", (int)M.NumRows(), (int)M.NumCols()); 30 | return false; 31 | } 32 | for (int i=0; i<4; i++) for (int j=0; j<4; j++) 33 | if (M[i][j] != res1[i][j]) { 34 | printf("M[4,4], expected M[%d][%d]=%x but found %x\n", 35 | i, j, res1[i][j], M[i][j]); 36 | return false; 37 | } 38 | 39 | resize(M,2,5); 40 | M.setFromBytes(buf); 41 | // check internal storage 42 | for (size_t i=0; i<2; i++) for (size_t j=0; j<2; j++) { 43 | if (M.rows[i].rep[j] != rep[2*i +j]) { 44 | printf("M[2,5], input byte %x, expected %x but found %x\n", 45 | buf[i], rep[i], M.rows[i].rep[0]); 46 | return false; 47 | } 48 | } 49 | // check matrix content 50 | if (M.NumRows()!=2 || M.NumCols()!=5) { 51 | printf("expected 2x5 matrix but found %dx%d", (int)M.NumRows(), (int)M.NumCols()); 52 | return false; 53 | } 54 | for (int i=0; i<2; i++) for (int j=0; j<5; j++) 55 | if (M[i][j] != res2[i][j]) { 56 | printf("M[2,5], expected M[%d][%d]=%x but found %x\n", 57 | i, j, res1[i][j], M[i][j]); 58 | return false; 59 | } 60 | 61 | bool thrown = false; 62 | try { // this should throw 63 | auto i = M.at(0).at(5); 64 | } catch (std::out_of_range e) { 65 | thrown = true; 66 | // std::cout << e.what() << std::endl; 67 | } 68 | if (!thrown) return false; 69 | thrown = false; 70 | try { // this should throw 71 | auto i = M.at(2).at(0); 72 | } catch (std::out_of_range e) { 73 | thrown = true; 74 | // std::cout << e.what() << std::endl; 75 | } 76 | if (!thrown) return false; 77 | return true; 78 | } 79 | 80 | // Testsing TernaryEMatrix, GF(p^4) 81 | bool basicTests2() { 82 | // {12,f7,59,f6} = [00 01 00 10 11 11 01 11 01 01 10 01 11 11 01 10] 83 | // {a3,83,e1,02} = [10 10 00 11 10 00 00 11 11 10 00 01 00 00 00 10] 84 | // {25,1d,12,78} = [00 10 01 01 00 01 11 01 00 01 00 10 01 11 10 00] 85 | // {66,9b,c3,92} = [01 10 01 10 10 01 10 11 11 00 00 11 10 01 00 10] 86 | // The bits that are actually stored should be 87 | // {12,04,59,06} = [00 01 00 10 00 00 01 00 01 01 10 01 00 00 01 10] 88 | // {a0,80,21,02} = [10 10 00 00 10 00 00 00 00 10 00 01 00 00 00 10] 89 | // {25,11,12,48} = [00 10 01 01 00 01 00 01 00 01 00 10 01 00 10 00] 90 | // {66,98,00,92} = [01 10 01 10 10 01 10 00 00 00 00 00 10 01 00 10] 91 | // yealds, e.g., 4-by-4 matrix [-1,0,1,0] [0,1,0,0] [1,-1,1,1][-1,1,0,0] 92 | // [0,0,-1,-1][0,0,0,-1][1,0,-1,0][-1,0,0,0] 93 | // [1,1,-1,0] [1,0,1,0] [-1,0,1,0][0,-1,0,1] 94 | // [-1,1,-1,1][0,-1,1,-1][0,0,0,0][-1,0,1,-1] 95 | // = [[-1+X^2-X^3 X^2+X^3 1-X-X^2-X^3 -X+X^3] 96 | // [X^2 1-X^3 X^2+X^3 -X-X^3] 97 | // [1+X-X^2 -1 1-X+X^2 1 ] 98 | // [-1-X-X^3 1-X^2 X^3 X^2-X^3] 99 | // ] 100 | unsigned char buf[] = {0x12, 0xf7, 0x59, 0xf6, 101 | 0xa3, 0x83, 0xe1, 0x02, 102 | 0x25, 0x1d, 0x12, 0x78, 103 | 0x66, 0x9b, 0xc3, 0x92}; 104 | 105 | TernaryEMatrix M; 106 | M.setFromBytes(buf,4,4); 107 | if (M.NumRows()!=4 || M.NumCols()!=4) { 108 | printf("basicTests2: expected a 4x4 matrix but found %dx%d", (int)M.NumRows(), (int)M.NumCols()); 109 | return false; 110 | } 111 | 112 | EMatrix expected; 113 | resize(expected, 4, 4); 114 | std::stringstream("[16 0 1 16]") >> expected[0][0]; // -1+X^2-X^3 115 | std::stringstream("[0 0 1 1]") >> expected[0][1]; // X^2+X^3 116 | std::stringstream("[1 16 16 16]")>> expected[0][2]; // 1-X-X^2-X^3 117 | std::stringstream("[0 16 0 1]") >> expected[0][3]; // -X+X^3 118 | 119 | std::stringstream("[0 0 1]") >> expected[1][0]; // X^2 120 | std::stringstream("[1 0 0 16]") >> expected[1][1]; // 1-X^3 121 | std::stringstream("[0 0 1 1]") >> expected[1][2]; // X^2+X^3 122 | std::stringstream("[0 16 0 16]") >> expected[1][3]; // -X-X^3 123 | 124 | std::stringstream("[1 1 16]") >> expected[2][0]; // 1+X-X^2 125 | std::stringstream("[16]") >> expected[2][1]; // -1 126 | std::stringstream("[1 16 1]") >> expected[2][2]; // 1-X+X^2 127 | std::stringstream("[1]") >> expected[2][3]; // 1 128 | 129 | std::stringstream("[16 16 0 16]")>> expected[3][0]; // -1-X-X^3 130 | std::stringstream("[1 0 16]") >> expected[3][1]; // 1-X^2 131 | std::stringstream("[0 0 0 1]") >> expected[3][2]; // X^3 132 | std::stringstream("[0 0 1 16]") >> expected[3][3]; // X^2-X^3 133 | for (int i=0; i<4; i++) for (int j=0; j<4; j++) { 134 | if (expected[i][j] != M(i,j)) { 135 | std::cout << "basicTests2 expected M["<(1L)<<252) 238 | // + NTL::conv("27742317777372353535851937790883648493")); 239 | NTL::ZZ_p::init(NTL::conv(17)); 240 | NTL::ZZ_pX px; // set to X^4 +1 241 | NTL::SetCoeff(px, 0); 242 | NTL::SetCoeff(px, 4); 243 | NTL::ZZ_pE::init(px); 244 | TernaryEMatrix::init(); 245 | 246 | if (!basicTests() || !testMatMul() || !basicTests2() ||!testMatMul2()) 247 | std::cout << LWEVSS_TESTS::failed << std::endl; 248 | else 249 | std::cout << LWEVSS_TESTS::passed << std::endl; 250 | } 251 | --------------------------------------------------------------------------------