diff --git a/.github/workflows/binds-go.yml b/.github/workflows/binds-go.yml index ccda3c190..71a4ffe00 100644 --- a/.github/workflows/binds-go.yml +++ b/.github/workflows/binds-go.yml @@ -32,15 +32,10 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version - - name: Prepare build system for macOS - if: startsWith(matrix.os, 'macos') - run: | - brew install gmp - - name: Build library using CMake run: | cores=$(getconf _NPROCESSORS_ONLN) diff --git a/.github/workflows/binds-py.yml b/.github/workflows/binds-py.yml index 803fb37fa..b305a3fff 100644 --- a/.github/workflows/binds-py.yml +++ b/.github/workflows/binds-py.yml @@ -92,11 +92,6 @@ jobs: - name: Build the sdist run: uv build --sdist --out-dir dist - - name: Prepare build system - run: | - sudo apt-get update - sudo apt-get install -qq --yes libgmp-dev - - name: Install from the sdist and run the unit tests run: | uv venv .venv-sdist diff --git a/.github/workflows/binds-rs.yml b/.github/workflows/binds-rs.yml index 8e3db323d..5656b3227 100644 --- a/.github/workflows/binds-rs.yml +++ b/.github/workflows/binds-rs.yml @@ -27,15 +27,10 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version - - name: Prepare build system for macOS - if: startsWith(matrix.os, 'macos') - run: | - brew install gmp - - name: Install Rust run: | rustup toolchain install ${{ matrix.rust }} @@ -47,3 +42,5 @@ jobs: run: | cd rust-bindings/bls-dash-sys cargo test + cd ../bls-signatures + cargo test diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 8b97fad98..8b253e05e 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -17,7 +17,7 @@ concurrency: jobs: build: - name: ${{ matrix.os }}, ${{ matrix.compiler.cc }}, ${{ matrix.backend }} backend + name: ${{ matrix.os }}, ${{ matrix.compiler.cc }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -28,7 +28,6 @@ jobs: cxx: g++ - cc: clang cxx: clang++ - backend: [ easy, gmp ] steps: - name: Checkout code uses: actions/checkout@v3 @@ -37,21 +36,20 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version - name: Prepare build system for macOS if: startsWith(matrix.os, 'macos') run: | - ls -l export MACOSX_DEPLOYMENT_TARGET=10.14 - brew install gmp pkg-config + brew install pkg-config - name: Build library using CMake run: | mkdir -p build && cd build - CC=${{ matrix.compiler.cc }} CXX=${{ matrix.compiler.cxx }} cmake .. -DARITH=${{ matrix.backend }} -DBUILD_BLS_PYTHON_BINDINGS=0 -DCMAKE_BUILD_TYPE=Debug + CC=${{ matrix.compiler.cc }} CXX=${{ matrix.compiler.cxx }} cmake .. -DBUILD_BLS_PYTHON_BINDINGS=0 -DCMAKE_BUILD_TYPE=Debug cmake --build . -- -j 6 mv src/runtest .. diff --git a/.github/workflows/stale-issue.yml b/.github/workflows/stale-issue.yml old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore index f968c1dbe..ec8e23435 100644 --- a/.gitignore +++ b/.gitignore @@ -17,15 +17,6 @@ main .o obj/ src/*.o -depends/relic/CTestTestfile.cmake -depends/relic/bench/CTestTestfile.cmake -depends/relic/bin -depends/relic/include/relic_conf.h -depends/relic/include/relic_conf.h.in -depends/relic/include/relic_conf.h.old -depends/relic/include/stamp-h1 -depends/relic/test/CTestTestfile.cmake -contrib/gmp-6.1.2/ .idea .vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index a224da20b..b99a9f750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,12 @@ endif() project(BLS) +if(MSVC) + enable_language(ASM_MASM) +else() + enable_language(ASM) +endif() + set(BUILD_BLS_JS_BINDINGS "1" CACHE STRING "") set(BUILD_BLS_PYTHON_BINDINGS "1" CACHE STRING "") set(BUILD_BLS_TESTS "1" CACHE STRING "") @@ -29,94 +35,27 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules ) -# Relic related options - -set(STBIN "off" CACHE STRING "Relic - Build static binaries") - -find_package(gmp) -if (GMP_FOUND) - message(STATUS "Found libgmp") - set(ARITH "gmp" CACHE STRING "") -else() - set(ARITH "easy" CACHE STRING "") -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(WSIZE "32" CACHE STRING "Relic - Processor word size") -else() - set(WSIZE "64" CACHE STRING "Relic - Processor word size") -endif() - -if(EMSCRIPTEN) - # emscripten needs arch set to be none since it can't compile assembly - set(ARCH "" CACHE STRING "") - # emscripten is a 32 bit compiler - set(WSIZE "32" CACHE STRING "Relic - Processor word size") -endif() - -if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(TIMER "ANSI" CACHE STRING "") - set(MULTI "OPENMP" CACHE STRING "") -else() - set(TIMER "CYCLE" CACHE STRING "") - set(MULTI "PTHREAD" CACHE STRING "") -endif() - -set(CHECK "off" CACHE STRING "") -set(VERBS "off" CACHE STRING "") -set(ALLOC "AUTO" CACHE STRING "") -set(SHLIB "off" CACHE STRING "") -set(DOCUM "off" CACHE STRING "") -set(FP_PRIME "381" CACHE STRING "Relic - Prime modulus size") - -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(SEED "UDEV" CACHE STRING "") - set(FP_QNRES "off" CACHE STRING "") -elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(SEED "WCGR" CACHE STRING "") - set(FP_QNRES "on" CACHE STRING "") -else() - set(SEED "UDEV" CACHE STRING "") - set(FP_QNRES "on" CACHE STRING "") -endif() - -set(FP_METHD "INTEG;INTEG;INTEG;MONTY;LOWER;SLIDE" CACHE STRING "") - -if(MSVC) - set(COMP_FLAGS "" CACHE STRING "") -else() - set(COMP_FLAGS "-O3 -funroll-loops -fomit-frame-pointer" CACHE STRING "") -endif() - -set(FP_PMERS "off" CACHE STRING "") -set(FPX_METHD "INTEG;INTEG;LAZYR" CACHE STRING "") -set(EP_PLAIN "off" CACHE STRING "") -set(EP_SUPER "off" CACHE STRING "") -# Disable relic tests and benchmarks -set(TESTS "0" CACHE STRING "Relic - Number of times each test is ran") -set(BENCH "0" CACHE STRING "Relic - Number of times each benchmark is ran") - -set(QUIET "on" CACHE STRING "Relic - Build with printing disabled") - -set(PP_EXT "LAZYR" CACHE STRING "") -set(PP_METHD "LAZYR;OATEP" CACHE STRING "") - # Disable mimalloc shared libraries and tests, enable secure mode set(MI_SECURE "on" CACHE STRING "") set(MI_BUILD_SHARED "off" CACHE STRING "") set(MI_BUILD_TESTS "off" CACHE STRING "") set(MI_OVERRIDE "off" CACHE STRING "") - -add_subdirectory(depends/relic) add_subdirectory(depends/mimalloc) -#message(STATUS "Patching Relic to make setjmp.h inclusion conditional") -# -#execute_process( -# COMMAND bash -c "git apply ${CMAKE_SOURCE_DIR}/setjmp_patch.diff" -# WORKING_DIRECTORY ${RELIC_SRC} -#) +include(FetchContent) + +set(BLST_GIT_TAG "a8cd361c9f671577aeab3f074098443af92a53fc") +set(BLST_REPOSITORY "https://github.com/supranational/blst") + +message(STATUS "blst will be built from: ${BLST_GIT_TAG} and repository ${BLST_REPOSITORY}") + +FetchContent_Declare( + blst + GIT_REPOSITORY ${BLST_REPOSITORY} + GIT_TAG ${BLST_GIT_TAG} +) +FetchContent_MakeAvailable(blst) add_subdirectory(src) @@ -128,14 +67,7 @@ endif() file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES};") -if(GMP_INCLUDES) - file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${GMP_INCLUDES};") -endif() - -# Write gmp library path for rust binding -if(GMP_LIBRARIES) - file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/gmp_libraries.txt" "${GMP_LIBRARIES}") -endif() +file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${blst_SOURCE_DIR}/bindings;") if(EMSCRIPTEN) if(BUILD_BLS_JS_BINDINGS) diff --git a/README.md b/README.md index 291b83499..d3292e46d 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,19 @@ > **This library has not undergone a formal security review.** `bls-signatures` is a cross-platform library implementing BLS12-381 primitives for Dash built on -the [`relic`](https://github.com/relic-toolkit/relic) toolkit with bindings available in +the [`blst library`](https://github.com/supranational/blst.git) toolkit with bindings available in [Python](./binds/python), [Rust](./rust-bindings/), [Go](./go-bindings/) and [Javascript](./js-bindings). ## Dependencies * A C++17 capable compiler (GCC 9, Clang 7 or higher) * CMake 3.18 or higher -* [`libgmp`](https://gmplib.org/) (for fast arithmetic, **optional**) Additionally, the following dependencies are supplied by the codebase * [`catch2`](https://github.com/catchorg/Catch2) (for tests) * [`mimalloc`](https://github.com/microsoft/mimalloc) (for secure memory operations) -* [`relic`](https://github.com/relic-toolkit/relic) (for cryptographic operations) +* [`blst`](https://github.com/supranational/blst.git) (for cryptographic operations) ## Build library @@ -100,6 +99,11 @@ uv run pytest -v binds/python/test_unit.py uv run pytest -v binds/python/test_bench.py --benchmark-only ``` +## BLST license + +BLST is used with the +[Apache 2.0 license](https://github.com/supranational/blst/blob/master/LICENSE) + ## License ```text diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index 01b562cf4..ad9f3b980 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -1,337 +1,87 @@ -#!/bin/sh -set -x +#!/bin/bash +# +# Builds libdashbls (with the blst backend CMake fetches) for Apple platforms, +# for consumption by rust-bindings' "apple" feature. +# +# Usage: apple.rust.deps.sh +# +# Artefacts are placed in build/artefacts//: libdashbls.a and +# libmimalloc-secure.a. The former contains the blst objects; no external +# arithmetic library (the relic-era GMP dependency is gone). + set -e -# "x86_64-apple-ios" -# "aarch64-apple-ios" -# "aarch64-apple-ios-sim" -# "x86_64-apple-darwin" -# "aarch64-apple-darwin" -# TODO: it's probably needs to be optimized in order to increase better build velocity -# TODO: so we need to combine multiple targets + TARGET=$1 -git submodule update --init -MIN_IOS="13.0" -MIN_WATCHOS="5.0" -MIN_TVOS=$MIN_IOS -MIN_MACOS="10.15" -IPHONEOS=iphoneos -IPHONESIMULATOR=iphonesimulator -WATCHOS=watchos -WATCHSIMULATOR=watchsimulator -TVOS=appletvos -TVSIMULATOR=appletvsimulator -MACOS=macosx +MACOS="macosx" +IPHONEOS="iphoneos" +IPHONESIMULATOR="iphonesimulator" -LOGICALCPU_MAX=$(sysctl -n hw.logicalcpu_max) -BUILD=build - -version_min_flag() { - PLATFORM=$1 - FLAG="" - # shellcheck disable=SC2039 - # shellcheck disable=SC2053 - if [[ $PLATFORM = $IPHONEOS ]]; then - FLAG="-miphoneos-version-min=${MIN_IOS}" - elif [[ $PLATFORM = $IPHONESIMULATOR ]]; then - FLAG="-mios-simulator-version-min=${MIN_IOS}" - elif [[ $PLATFORM = $WATCHOS ]]; then - FLAG="-mwatchos-version-min=${MIN_WATCHOS}" - elif [[ $PLATFORM = $WATCHSIMULATOR ]]; then - FLAG="-mwatchos-simulator-version-min=${MIN_WATCHOS}" - elif [[ $PLATFORM = $TVOS ]]; then - FLAG="-mtvos-version-min=${MIN_TVOS}" - elif [[ $PLATFORM = $TVSIMULATOR ]]; then - FLAG="-mtvos-simulator-version-min=${MIN_TVOS}" - elif [[ $PLATFORM = $MACOS ]]; then - FLAG="-mmacosx-version-min=${MIN_MACOS}" - fi - echo $FLAG -} +BUILD="build" +LOGICALCPU_MAX=$(sysctl -n hw.logicalcpu_max) prepare() { - download_gmp() { - GMP_VERSION="6.3.0" - CURRENT_DIR=$(pwd) - echo "$CURRENT_DIR" - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - mkdir -p "contrib" - if [ ! -s "contrib/gmp-${GMP_VERSION}.tar.bz2" ]; then - curl -L -o "contrib/gmp-${GMP_VERSION}.tar.bz2" https://ftp.gnu.org/gnu/gmp/gmp-${GMP_VERSION}.tar.bz2 - fi - rm -rf "contrib/gmp" - # shellcheck disable=SC2039,SC2164 - pushd contrib - tar xfj "gmp-${GMP_VERSION}.tar.bz2" - mv gmp-${GMP_VERSION} gmp - # shellcheck disable=SC2039,SC2164 - popd #contrib - # shellcheck disable=SC2039,SC2164 - popd #build - } + # shellcheck disable=SC2039,SC2164 + pushd ${BUILD} download_cmake_toolchain() { - if [ ! -s "${BUILD}/ios.toolchain.cmake" ]; then + if [ ! -s "ios.toolchain.cmake" ]; then SHA256_HASH="d02857ff6bd64f1d7109ca59c3e4f3b2f89d0663c412146e6977c679801b3243" - curl -o "${BUILD}/ios.toolchain.cmake" https://raw.githubusercontent.com/leetal/ios-cmake/c55677a4445b138c9ef2650d3c21f22cc78c2357/ios.toolchain.cmake - DOWNLOADED_HASH=$(shasum -a 256 ${BUILD}/ios.toolchain.cmake | cut -f 1 -d " ") - if [ $SHA256_HASH != "$DOWNLOADED_HASH" ]; then + echo "Downloading ios.toolchain.cmake" + curl -o "ios.toolchain.cmake" https://raw.githubusercontent.com/leetal/ios-cmake/c55677a4445b138c9ef2650d3c21f22cc78c2357/ios.toolchain.cmake + DOWNLOADED_HASH=$(shasum -a 256 ios.toolchain.cmake | cut -f 1 -d " ") + if [ "$SHA256_HASH" != "$DOWNLOADED_HASH" ]; then echo "Error: sha256 checksum of ios.toolchain.cmake mismatch" >&2 exit 1 fi fi } - download_relic() { - CURRENT_DIR=$(pwd) - echo "$CURRENT_DIR" - mkdir -p "${CURRENT_DIR}/${BUILD}/depends" - if [ ! -s "${CURRENT_DIR}/${BUILD}/depends/relic" ]; then - # shellcheck disable=SC2039,SC2164 - pushd "${CURRENT_DIR}/${BUILD}/depends" - git clone --depth 1 --branch "feat/ios-support" https://github.com/pankcuf/relic - # shellcheck disable=SC2039,SC2164 - pushd relic - git fetch --depth 1 origin 19fb6d79a77ade4ae8cd70d2b0ef7aab8720d1ae - git checkout 19fb6d79a77ade4ae8cd70d2b0ef7aab8720d1ae - # shellcheck disable=SC2039,SC2164 - popd #relic - # shellcheck disable=SC2039,SC2164 - popd #depends - fi - } - rm -rf ${BUILD} - mkdir -p ${BUILD} - download_relic - download_gmp download_cmake_toolchain -} -build_gmp_arch() { - PLATFORM=$1 - ARCH=$2 - PFX=${PLATFORM}-${ARCH} - # why this works with this host only? - HOST=aarch64-apple-darwin - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - PLATFORM_PATH=$(xcrun --sdk "$PLATFORM" --show-sdk-platform-path) - CLANG=$(xcrun --sdk "$PLATFORM" --find clang) - DEVELOPER=$(xcode-select --print-path) - CURRENT_DIR=$(pwd) - export PATH="${PLATFORM_PATH}/Developer/usr/bin:${DEVELOPER}/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin" - mkdir gmplib-"${PLATFORM}"-"${ARCH}" - CFLAGS="-Wno-unused-value -fembed-bitcode -arch ${ARCH} --sysroot=${SDK} $(version_min_flag "$PLATFORM")" - CONFIGURESCRIPT="gmp_configure_script.sh" - # shellcheck disable=SC2039,SC2164 - pushd contrib - # shellcheck disable=SC2039,SC2164 - pushd gmp - make clean || true - make distclean || true - echo "HOST: $HOST" - echo "PREFIX: ${CURRENT_DIR}/gmplib-${PFX}" - - cat >"$CONFIGURESCRIPT" << EOF -#!/bin/sh -./configure \ -CC="$CLANG" CFLAGS="$CFLAGS" CPPFLAGS="$CFLAGS" LDFLAGS="$CFLAGS" \ ---host=${HOST} --prefix="${CURRENT_DIR}/gmplib-${PFX}" \ ---disable-shared --enable-static --disable-assembly -v -EOF - - chmod a+x "$CONFIGURESCRIPT" - sh "$CONFIGURESCRIPT" - rm "$CONFIGURESCRIPT" - - # shellcheck disable=SC2039 - mkdir -p "${CURRENT_DIR}/log" - # shellcheck disable=SC2039 - make -j "$LOGICALCPU_MAX" &> "${CURRENT_DIR}"/log/gmplib-"${PFX}"-build.log - # shellcheck disable=SC2039 - make install &> "${CURRENT_DIR}"/log/gmplib-"${PFX}"-install.log - #make check - #exit 1 - # shellcheck disable=SC2039,SC2164 - popd # gmp - # shellcheck disable=SC2039,SC2164 - popd # contrib # shellcheck disable=SC2039,SC2164 popd # build } -build_relic_arch() { +# Maps (platform, arch) onto the leetal/ios-cmake PLATFORM value. +cmake_platform() { PLATFORM=$1 ARCH=$2 - PFX=${PLATFORM}-${ARCH} - - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - - BUILDDIR=relic-"${PFX}" - TOOLCHAIN=$(pwd)/ios.toolchain.cmake - GMP_PFX=$(pwd)/gmplib-${PFX} - rm -rf "$BUILDDIR" - mkdir "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - pushd "$BUILDDIR" - - unset CC - # shellcheck disable=SC2155 - export CC=$(xcrun --sdk "${PLATFORM}" --find clang) - - WSIZE=0 - IOS_PLATFORM="" - OPTIMIZATIONFLAGS="" - DEPLOYMENT_TARGET="" - - # shellcheck disable=SC2039 - # shellcheck disable=SC2053 - if [[ $PLATFORM = $IPHONEOS ]]; then - if [[ $ARCH = "arm64" ]] || [[ $ARCH = "arm64e" ]]; then - IOS_PLATFORM=OS64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - else - IOS_PLATFORM=OS - WSIZE=32 - fi - elif [[ $PLATFORM = $IPHONESIMULATOR ]]; then - if [[ $ARCH = "x86_64" ]]; then - IOS_PLATFORM=SIMULATOR64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $ARCH = "arm64" ]]; then - IOS_PLATFORM=SIMULATORARM64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - else - IOS_PLATFORM=SIMULATOR - WSIZE=32 - fi - elif [[ $PLATFORM = $WATCHOS ]]; then - IOS_PLATFORM=WATCHOS - DEPLOYMENT_TARGET=$MIN_WATCHOS - WSIZE=32 - elif [[ $PLATFORM = $WATCHSIMULATOR ]]; then - IOS_PLATFORM=SIMULATOR_WATCHOS - DEPLOYMENT_TARGET=$MIN_WATCHOS - WSIZE=32 - elif [[ $PLATFORM = $TVOS ]]; then - IOS_PLATFORM=TVOS - DEPLOYMENT_TARGET=$MIN_TVOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $PLATFORM = $TVSIMULATOR ]]; then - IOS_PLATFORM=SIMULATOR_TVOS - #TODO - if [[ $ARCH = "arm64" ]] - then - IOS_PLATFORM=OS64 - fi - DEPLOYMENT_TARGET=$MIN_TVOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $PLATFORM = $MACOS ]]; then - WSIZE=64 - IOS_PLATFORM=MAC - if [[ $ARCH = "arm64" ]] - then - IOS_PLATFORM=MAC_ARM64 - fi - DEPLOYMENT_TARGET=$MIN_MACOS - OPTIMIZATIONFLAGS=-fomit-frame-pointer + if [[ $PLATFORM = "$IPHONEOS" ]]; then + echo "OS64" + elif [[ $PLATFORM = "$IPHONESIMULATOR" && $ARCH = "x86_64" ]]; then + echo "SIMULATOR64" + elif [[ $PLATFORM = "$IPHONESIMULATOR" && $ARCH = "arm64" ]]; then + echo "SIMULATORARM64" + elif [[ $PLATFORM = "$MACOS" && $ARCH = "x86_64" ]]; then + echo "MAC" + elif [[ $PLATFORM = "$MACOS" && $ARCH = "arm64" ]]; then + echo "MAC_ARM64" fi - - COMPILER_ARGS="$(version_min_flag "$PLATFORM") -Wno-unused-functions" - - EXTRA_ARGS="-DOPSYS=NONE -DPLATFORM=$IOS_PLATFORM -DDEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN" - - # shellcheck disable=SC2039 - if [[ $ARCH = "i386" ]]; then - EXTRA_ARGS+=" -DARCH=X86" - elif [[ $ARCH = "x86_64" ]]; then - EXTRA_ARGS+=" -DARCH=X64" - elif [[ $ARCH = "arm64" ]]; then - # Relic doesn't support aarch64 yet, "ARCH=ARM" is for ARM 32-bit architecture only - EXTRA_ARGS+=" -DIOS_ARCH=arm64 -DARCH=" - elif [[ $ARCH = "armv7s" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=armv7s -DARCH=ARM" - elif [[ $ARCH = "armv7k" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=armv7k -DARCH=ARM" - elif [[ $ARCH = "arm64_32" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=arm64_32 -DARCH=ARM" - fi - - CURRENT_DIR=$(pwd) - cmake -DCMAKE_PREFIX_PATH:PATH="${GMP_PFX}" -DTESTS=0 -DBENCH=0 -DBUILD_BLS_JS_BINDINGS=0 -DBUILD_BLS_PYTHON_BINDINGS=0 \ - -DBUILD_BLS_BENCHMARKS=0 -DBUILD_BLS_TESTS=0 -DCHECK=off -DARITH=gmp -DTIMER=HPROC -DFP_PRIME=381 -DMULTI=PTHREAD \ - -DFP_QNRES=on -DFP_METHD="INTEG;INTEG;INTEG;MONTY;EXGCD;SLIDE" -DFPX_METHD="INTEG;INTEG;LAZYR" -DPP_METHD="LAZYR;OATEP" \ - -DCOMP_FLAGS="-pipe -std=c99 -O3 -funroll-loops $OPTIMIZATIONFLAGS -isysroot $SDK -arch $ARCH -fembed-bitcode ${COMPILER_ARGS}" \ - -DWSIZE=$WSIZE -DVERBS=off -DSHLIB=off -DALLOC="AUTO" -DEP_PLAIN=off -DEP_SUPER=off -DPP_EXT="LAZYR" \ - -DWITH="DV;BN;MD;FP;EP;FPX;EPX;PP;PC;CP" -DBN_METHD="COMBA;COMBA;MONTY;SLIDE;STEIN;BASIC" ${EXTRA_ARGS} ../../ - - make -j "$LOGICALCPU_MAX" - # shellcheck disable=SC2039,SC2164 - popd # "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - popd # depends/relic } build_bls_arch() { - # shellcheck disable=SC2039 - BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "legacy" "privatekey" "schemes" "threshold" ) - # shellcheck disable=SC2039 - ALL_BLS_OBJ_FILES=$(printf "%s.o " "${BLS_FILES[@]}") - PLATFORM=$1 ARCH=$2 PFX=${PLATFORM}-${ARCH} - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - + CMAKE_PLATFORM=$(cmake_platform "$PLATFORM" "$ARCH") BUILDDIR=${BUILD}/bls-"${PFX}" - rm -rf "$BUILDDIR" - mkdir "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - pushd "$BUILDDIR" - - EXTRA_ARGS="$(version_min_flag "$PLATFORM")" - - CURRENT_DIR=$(pwd) - - # shellcheck disable=SC2039 - for F in "${BLS_FILES[@]}" - do - clang -I"../contrib/relic/include" \ - -I"../../depends/relic/include" \ - -I"../../include/dashbls" \ - -I"../relic-${PFX}/depends/relic/include" \ - -I"../../src/" \ - -I"../gmplib-${PFX}/include" \ - -x c++ -std=c++14 -stdlib=libc++ -fembed-bitcode -arch "${ARCH}" -isysroot "${SDK}" "${EXTRA_ARGS}" \ - -c "../../src/${F}.cpp" -o "${F}.o" - done - # shellcheck disable=SC2086 - xcrun -sdk "$PLATFORM" ar -cvq libbls.a $ALL_BLS_OBJ_FILES + rm -rf "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - popd # "$BUILDDIR" -} - -build_all_arch() { - PLATFORM=$1 - ARCH=$2 - build_gmp_arch "$PLATFORM" "$ARCH" - build_relic_arch "$PLATFORM" "$ARCH" - build_bls_arch "$PLATFORM" "$ARCH" + cmake -S . -B "$BUILDDIR" \ + -DCMAKE_TOOLCHAIN_FILE="$(pwd)/${BUILD}/ios.toolchain.cmake" \ + -DPLATFORM="${CMAKE_PLATFORM}" \ + -DDEPLOYMENT_TARGET=13.0 \ + -DENABLE_BITCODE=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_BLS_TESTS=0 \ + -DBUILD_BLS_BENCHMARKS=0 \ + -DBUILD_BLS_JS_BINDINGS=0 \ + -DBUILD_BLS_PYTHON_BINDINGS=0 + + cmake --build "$BUILDDIR" --parallel "$LOGICALCPU_MAX" --target dashbls } build_target() { @@ -356,18 +106,14 @@ build_target() { ARCH=arm64 PLATFORM=$MACOS fi - build_all_arch "$PLATFORM" "$ARCH" + build_bls_arch "$PLATFORM" "$ARCH" PFX="${PLATFORM}"-"${ARCH}" rm -rf "build/artefacts/${BUILD_IN}" mkdir -p "build/artefacts/${BUILD_IN}" - cp "build/gmplib-${PFX}/lib/libgmp.a" "build/artefacts/${BUILD_IN}" - cp "build/relic-${PFX}/depends/relic/lib/librelic_s.a" "build/artefacts/${BUILD_IN}" - cp "build/bls-${PFX}/libbls.a" "build/artefacts/${BUILD_IN}" -# cp -rf build/bls-"${PFX}"/*.o build/artefacts/"${BUILD_IN}"/include -# cp -rf src/*.hpp build/artefacts/"${BUILD_IN}"/include -# cp -rf build/gmplib-"${PFX}"/include/gmp.h build/artefacts/"${BUILD_IN}"/include -# cp -rf build/relic-"${PFX}"/_deps/relic-build/include/*.h build/artefacts/"${BUILD_IN}"/include + cp "build/bls-${PFX}/src/libdashbls.a" "build/artefacts/${BUILD_IN}" + cp "build/bls-${PFX}/depends/mimalloc/libmimalloc-secure.a" "build/artefacts/${BUILD_IN}" } +mkdir -p ${BUILD} prepare build_target "$TARGET" diff --git a/binds/python/CMakeLists.txt b/binds/python/CMakeLists.txt index 98a776e4b..9117ade8f 100644 --- a/binds/python/CMakeLists.txt +++ b/binds/python/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT pybind11_FOUND) FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.13.6 + GIT_TAG v2.13.6 ) FetchContent_MakeAvailable(pybind11) endif() diff --git a/binds/python/pythonbindings.cpp b/binds/python/pythonbindings.cpp index b037a74d6..0723e2b1b 100644 --- a/binds/python/pythonbindings.cpp +++ b/binds/python/pythonbindings.cpp @@ -657,7 +657,6 @@ PYBIND11_MODULE(dashbls, m) return other * self; }, py::is_operator()) - .def( "__repr__", [](const G2Element &ele) { diff --git a/cmake_modules/Findgmp.cmake b/cmake_modules/Findgmp.cmake deleted file mode 100644 index 3e59ede0a..000000000 --- a/cmake_modules/Findgmp.cmake +++ /dev/null @@ -1,103 +0,0 @@ -# Try to find the GMP library -# https://gmplib.org/ -# -# This module supports requiring a minimum version, e.g. you can do -# find_package(GMP 6.0.0) -# to require version 6.0.0 to newer of GMP. -# -# Once done this will define -# -# GMP_FOUND - system has GMP lib with correct version -# GMP_INCLUDES - the GMP include directory -# GMP_LIBRARIES - the GMP library -# GMP_VERSION - GMP version -# -# Copyright (c) 2016 Jack Poulson, -# Redistribution and use is allowed according to the terms of the BSD license. - -include(BrewHelper) -find_brew_prefix(_GMP_BREW_HINT gmp) - -find_path(GMP_INCLUDES - NAMES - gmp.h - PATHS - $ENV{GMPDIR} ${INCLUDE_INSTALL_DIR} - PATH_SUFFIXES - include - HINTS - ${_GMP_BREW_HINT} -) - -# Set GMP_FIND_VERSION to 5.1.0 if no minimum version is specified -if(NOT GMP_FIND_VERSION) - if(NOT GMP_FIND_VERSION_MAJOR) - set(GMP_FIND_VERSION_MAJOR 5) - endif() - if(NOT GMP_FIND_VERSION_MINOR) - set(GMP_FIND_VERSION_MINOR 1) - endif() - if(NOT GMP_FIND_VERSION_PATCH) - set(GMP_FIND_VERSION_PATCH 0) - endif() - set(GMP_FIND_VERSION - "${GMP_FIND_VERSION_MAJOR}.${GMP_FIND_VERSION_MINOR}.${GMP_FIND_VERSION_PATCH}") -endif() - -message("GMP_INCLUDES=${GMP_INCLUDES}") -if(GMP_INCLUDES) - # Since the GMP version macros may be in a file included by gmp.h of the form - # gmp-.*[_]?.*.h (e.g., gmp-x86_64.h), we search each of them. - file(GLOB GMP_HEADERS "${GMP_INCLUDES}/gmp.h" "${GMP_INCLUDES}/gmp-*.h") - foreach(gmp_header_filename ${GMP_HEADERS}) - file(READ "${gmp_header_filename}" _gmp_version_header) - string(REGEX MATCH - "define[ \t]+__GNU_MP_VERSION[ \t]+([0-9]+)" _gmp_major_version_match - "${_gmp_version_header}") - if(_gmp_major_version_match) - set(GMP_MAJOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_MINOR[ \t]+([0-9]+)" - _gmp_minor_version_match "${_gmp_version_header}") - set(GMP_MINOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_PATCHLEVEL[ \t]+([0-9]+)" - _gmp_patchlevel_version_match "${_gmp_version_header}") - set(GMP_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") - set(GMP_VERSION - ${GMP_MAJOR_VERSION}.${GMP_MINOR_VERSION}.${GMP_PATCHLEVEL_VERSION}) - endif() - endforeach() - - # Check whether found version exists and exceeds the minimum requirement - if(NOT GMP_VERSION) - set(GMP_VERSION_OK FALSE) - message(STATUS "GMP version was not detected") - elseif(${GMP_VERSION} VERSION_LESS ${GMP_FIND_VERSION}) - set(GMP_VERSION_OK FALSE) - message(STATUS "GMP version ${GMP_VERSION} found in ${GMP_INCLUDES}, " - "but at least version ${GMP_FIND_VERSION} is required") - else() - set(GMP_VERSION_OK TRUE) - endif() -endif() - -if(STBIN) - set(_gmp_lib_name libgmp.a) -else() - set(_gmp_lib_name libgmp.so) -endif() - -find_library(GMP_LIBRARIES - NAMES - ${_gmp_lib_name} gmp.lib libgmp-10 libgmp gmp - HINTS - ${_GMP_BREW_HINT} - INCLUDE_DIRS - ${GMP_INCLUDES} - PATHS - $ENV{GMPDIR} ${LIB_INSTALL_DIR} -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(gmp DEFAULT_MSG - GMP_INCLUDES GMP_LIBRARIES GMP_VERSION_OK) -mark_as_advanced(GMP_INCLUDES GMP_LIBRARIES) diff --git a/go-bindings/Makefile b/go-bindings/Makefile index dcea3f24f..0c4e33b0e 100644 --- a/go-bindings/Makefile +++ b/go-bindings/Makefile @@ -12,23 +12,6 @@ BUILD_DIR=$(CURDIR)/../build CURR_DIR := $(dir $(MAKEFILE_PATH)) SRC_DIR=$(CURDIR)/../src -GMP_PREFIX := /usr/local - -ifeq ("$(OS)", "Darwin") - ifneq ($(wildcard /opt/local/bin/port),) - GMP_PREFIX := /opt/local - endif - ifeq ("$(ARCH)", "arm64") - ifneq ($(wildcard /opt/homebrew/bin/brew),) - GMP_PREFIX := /opt/homebrew - endif - endif -else ifeq ("$(OS)", "Linux") - ifneq ($(wildcard /home/linuxbrew/.linuxbrew/bin/brew),) - GMP_PREFIX := /home/linuxbrew/.linuxbrew - endif -endif - MIMALLOC_LIB := mimalloc-secure MIMALLOC_LIB_PATH := $(CURR_DIR)../build/depends/mimalloc @@ -36,18 +19,15 @@ ifneq ($(wildcard $(MIMALLOC_LIB_PATH)/libmimalloc-secure-debug.a),) MIMALLOC_LIB := mimalloc-secure-debug endif +# blst is compiled into libdashbls; its headers come from the CMake build CGO_CXXFLAGS ?= "\ --I$(CURR_DIR)../build/depends/relic/include \ +-I$(CURR_DIR)../build/_deps/blst-src/bindings \ -I$(CURR_DIR)../depends/mimalloc/include \ --I$(CURR_DIR)../depends/relic/include \ --I$(CURR_DIR)../include \ --I$(GMP_PREFIX)/include" +-I$(CURR_DIR)../include" CGO_LDFLAGS ?= "\ -L$(CURR_DIR)../build/src -ldashbls \ --L$(MIMALLOC_LIB_PATH) -l$(MIMALLOC_LIB) \ --L$(CURR_DIR)../build/depends/relic/lib -lrelic_s \ --L$(GMP_PREFIX)/lib -lgmp" +-L$(MIMALLOC_LIB_PATH) -l$(MIMALLOC_LIB)" .PHONY: default prepare fmt test cover vet help clean config diff --git a/include/dashbls/bls.hpp b/include/dashbls/bls.hpp index cd6922a58..d91088735 100644 --- a/include/dashbls/bls.hpp +++ b/include/dashbls/bls.hpp @@ -40,8 +40,6 @@ class BLS { static bool Init(); static void SetSecureAllocator(Util::SecureAllocCallback allocCb, Util::SecureFreeCallback freeCb); - - static void CheckRelicErrors(bool should_throw = true); }; } // end namespace bls diff --git a/include/dashbls/chaincode.hpp b/include/dashbls/chaincode.hpp index bd3e57b4d..ecd5db79f 100644 --- a/include/dashbls/chaincode.hpp +++ b/include/dashbls/chaincode.hpp @@ -15,19 +15,10 @@ #ifndef SRC_CHAINCODE_HPP_ #define SRC_CHAINCODE_HPP_ +#include #include #include -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - - -#include "relic.h" -#include "relic_test.h" - #include "util.hpp" namespace bls { @@ -51,9 +42,8 @@ class ChainCode { ChainCode() {} private: - bn_t chainCode; + std::array chainCode{}; }; } // end namespace bls #endif // SRC_CHAINCODE_HPP_ - diff --git a/include/dashbls/elements.hpp b/include/dashbls/elements.hpp index 95937f62d..cbc4154d5 100644 --- a/include/dashbls/elements.hpp +++ b/include/dashbls/elements.hpp @@ -16,17 +16,12 @@ #define SRC_BLSELEMENTS_HPP_ extern "C" { -#include "relic.h" +#include "blst.h" } -#include "relic_conf.h" -#include "util.hpp" - -#if defined GMP && ARITH == GMP -#include -#endif - #include +#include "util.hpp" + namespace bls { class G1Element; class G2Element; @@ -36,25 +31,27 @@ class G1Element { public: static const size_t SIZE = 48; - G1Element() { - g1_set_infty(p); - } - - static G1Element FromBytes(Bytes bytes, bool fLegacy = false); - static G1Element FromBytesUnchecked(Bytes bytes, bool fLegacy = false); - static G1Element FromByteVector(const std::vector &bytevec, bool fLegacy = false); - static G1Element FromNative(const g1_t element); - static G1Element FromMessage(const std::vector &message, - const uint8_t *dst, - int dst_len); - static G1Element FromMessage(Bytes message, - const uint8_t* dst, - int dst_len); + G1Element() { memset(&p, 0x00, sizeof(blst_p1)); } + + static G1Element FromBytes(Bytes bytes, bool legacy_encoding = false); + static G1Element FromBytesUnchecked(Bytes bytes, bool legacy_encoding = false); + static G1Element FromByteVector(const std::vector &bytevec, bool legacy_encoding = false); + static G1Element FromNative(const blst_p1 &element); + static G1Element FromAffine(const blst_p1_affine &element); + static G1Element FromMessage( + const std::vector &message, + const uint8_t *dst, + size_t dst_len); + static G1Element FromMessage( + Bytes message, + const uint8_t *dst, + size_t dst_len); static G1Element Generator(); bool IsValid() const; void CheckValid() const; - void ToNative(g1_t output) const; + void ToNative(blst_p1 *output) const; + void ToAffine(blst_p1_affine *output) const; G1Element Negate() const; GTElement Pair(const G2Element &b) const; uint32_t GetFingerprint(bool fLegacy = false) const; @@ -65,41 +62,41 @@ class G1Element { friend bool operator==(const G1Element &a, const G1Element &b); friend bool operator!=(const G1Element &a, const G1Element &b); friend std::ostream &operator<<(std::ostream &os, const G1Element &s); - friend G1Element& operator+=(G1Element& a, const G1Element& b); + friend G1Element &operator+=(G1Element &a, const G1Element &b); friend G1Element operator+(const G1Element &a, const G1Element &b); - friend G1Element operator*(const G1Element &a, const bn_t &k); - friend G1Element operator*(const bn_t &k, const G1Element &a); + friend G1Element operator*(const G1Element &a, const blst_scalar &k); + friend G1Element operator*(const blst_scalar &k, const G1Element &a); friend GTElement operator&(const G1Element &a, const G2Element &b); private: - g1_t p; + blst_p1 p; }; class G2Element { public: static const size_t SIZE = 96; - G2Element() { - g2_set_infty(q); - } - - static G2Element FromBytes(Bytes bytes, bool fLegacy = false); - static G2Element FromBytesUnchecked(Bytes bytes, bool fLegacy = false); - static G2Element FromByteVector(const std::vector &bytevec, bool fLegacy = false); - static G2Element FromNative(const g2_t element); - static G2Element FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len, - bool fLegacy = false); - static G2Element FromMessage(Bytes message, - const uint8_t* dst, - int dst_len, - bool fLegacy = false); + G2Element() { memset(&q, 0x00, sizeof(blst_p2)); } + + static G2Element FromBytes(Bytes bytes, bool legacy_encoding = false); + static G2Element FromBytesUnchecked(Bytes bytes, bool legacy_encoding = false); + static G2Element FromByteVector(const std::vector &bytevec, bool legacy_encoding = false); + static G2Element FromNative(const blst_p2 &element); + static G2Element FromAffine(const blst_p2_affine &element); + static G2Element FromMessage( + const std::vector &message, + const uint8_t *dst, + size_t dst_len, bool legacy_encoding = false); + static G2Element FromMessage( + Bytes message, + const uint8_t *dst, + size_t dst_len, bool legacy_encoding = false); static G2Element Generator(); bool IsValid() const; void CheckValid() const; - void ToNative(g2_t output) const; + void ToNative(blst_p2 *output) const; + void ToAffine(blst_p2_affine *output) const; G2Element Negate() const; GTElement Pair(const G1Element &a) const; std::vector Serialize(bool fLegacy = false) const; @@ -109,22 +106,25 @@ class G2Element { friend bool operator==(G2Element const &a, G2Element const &b); friend bool operator!=(G2Element const &a, G2Element const &b); friend std::ostream &operator<<(std::ostream &os, const G2Element &s); - friend G2Element& operator+=(G2Element& a, const G2Element& b); + friend G2Element &operator+=(G2Element &a, const G2Element &b); friend G2Element operator+(const G2Element &a, const G2Element &b); - friend G2Element operator*(const G2Element &a, const bn_t &k); - friend G2Element operator*(const bn_t &k, const G2Element &a); + friend G2Element operator*(const G2Element &a, const blst_scalar &k); + friend G2Element operator*(const blst_scalar &k, const G2Element &a); private: - g2_t q; + blst_p2 q; }; class GTElement { public: - static const size_t SIZE = 384; + static const size_t SIZE = sizeof(blst_fp12); + static GTElement FromBytes(Bytes bytes); static GTElement FromBytesUnchecked(Bytes bytes); static GTElement FromByteVector(const std::vector &bytevec); - static GTElement FromNative(const gt_t *element); + static GTElement FromNative(const blst_fp12 *element); + static GTElement FromAffine(const blst_p1_affine &element); + static GTElement FromAffine(const blst_p2_affine &element); static GTElement Unity(); // unity void Serialize(uint8_t *buffer) const; @@ -135,10 +135,9 @@ class GTElement { friend bool operator!=(GTElement const &a, GTElement const &b); friend std::ostream &operator<<(std::ostream &os, const GTElement &s); friend GTElement operator*(GTElement &a, GTElement &b); - GTElement &operator=(const GTElement &rhs); private: - gt_t r; + blst_fp12 r; GTElement() {} }; diff --git a/include/dashbls/extendedprivatekey.hpp b/include/dashbls/extendedprivatekey.hpp index aefdbf057..575256ab9 100644 --- a/include/dashbls/extendedprivatekey.hpp +++ b/include/dashbls/extendedprivatekey.hpp @@ -15,21 +15,13 @@ #ifndef SRC_EXTENDEDPRIVATEKEY_HPP_ #define SRC_EXTENDEDPRIVATEKEY_HPP_ -#include "relic_conf.h" - #include -#if defined GMP && ARITH == GMP -#include -#endif - #include "chaincode.hpp" #include "elements.hpp" #include "extendedpublickey.hpp" #include "privatekey.hpp" -#include "relic.h" -#include "relic_test.h" namespace bls { /* diff --git a/include/dashbls/extendedpublickey.hpp b/include/dashbls/extendedpublickey.hpp index b44d5c636..c1fbdf7bf 100644 --- a/include/dashbls/extendedpublickey.hpp +++ b/include/dashbls/extendedpublickey.hpp @@ -15,20 +15,12 @@ #ifndef SRC_EXTENDEDPUBLICKEY_HPP_ #define SRC_EXTENDEDPUBLICKEY_HPP_ -#include "relic_conf.h" - #include -#if defined GMP && ARITH == GMP -#include -#endif - #include "chaincode.hpp" #include "elements.hpp" -#include "relic.h" -#include "relic_test.h" namespace bls { diff --git a/include/dashbls/hdkeys.hpp b/include/dashbls/hdkeys.hpp index 629ec5e3d..1cadd7423 100644 --- a/include/dashbls/hdkeys.hpp +++ b/include/dashbls/hdkeys.hpp @@ -15,16 +15,11 @@ #ifndef SRC_BLSHDKEYS_HPP_ #define SRC_BLSHDKEYS_HPP_ -#include "relic_conf.h" #include -#if defined GMP && ARITH == GMP -#include -#endif - -#include "util.hpp" -#include "privatekey.hpp" #include "hkdf.hpp" +#include "privatekey.hpp" +#include "util.hpp" namespace bls { @@ -32,14 +27,14 @@ class HDKeys { /** * Implements HD keys as specified in EIP2333. **/ - public: +public: static const uint8_t HASH_LEN = 32; static PrivateKey KeyGen(const std::vector& seed) { return KeyGen(Bytes(seed)); } - + static PrivateKey KeyGen(const Bytes& seed) { // KeyGen @@ -56,65 +51,44 @@ class HDKeys { throw std::invalid_argument("Seed size must be at least 32 bytes"); } - // "BLS-SIG-KEYGEN-SALT-" in ascii - const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, - 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; + blst_scalar* skBn = Util::SecAlloc(1); + blst_keygen_v3(skBn, seed.begin(), seed.size(), info, infoLen); + uint8_t* skBytes = Util::SecAlloc(32); + blst_bendian_from_scalar(skBytes, skBn); - uint8_t *prk = Util::SecAlloc(32); - uint8_t *ikmHkdf = Util::SecAlloc(seed.size() + 1); - memcpy(ikmHkdf, seed.begin(), seed.size()); - ikmHkdf[seed.size()] = 0; - - const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the - // order of the BLS 12-381 curve - - uint8_t *okmHkdf = Util::SecAlloc(L); - - uint8_t keyInfoHkdf[infoLen + 2]; - memcpy(keyInfoHkdf, info, infoLen); - keyInfoHkdf[infoLen] = 0; // Two bytes for L, 0 and 48 - keyInfoHkdf[infoLen + 1] = L; - - HKDF256::ExtractExpand( - okmHkdf, - L, - ikmHkdf, - seed.size() + 1, - saltHkdf, - 20, - keyInfoHkdf, - infoLen + 2); - - bn_t order; - bn_new(order); - g1_get_ord(order); - - // Make sure private key is less than the curve order - bn_t *skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, okmHkdf, L); - bn_mod_basic(*skBn, *skBn, order); - - uint8_t *skBytes = Util::SecAlloc(32); - bn_write_bin(skBytes, 32, *skBn); PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); - Util::SecFree(prk); - Util::SecFree(ikmHkdf); Util::SecFree(skBn); - Util::SecFree(okmHkdf); Util::SecFree(skBytes); return k; } - static void IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t ikmLen, const uint8_t* salt, size_t saltLen) { + static void IKMToLamportSk( + uint8_t* outputLamportSk, + const uint8_t* ikm, + size_t ikmLen, + const uint8_t* salt, + size_t saltLen) + { // Expands the ikm to 255*HASH_LEN bytes for the lamport sk const uint8_t info[1] = {0}; - HKDF256::ExtractExpand(outputLamportSk, HASH_LEN * 255, ikm, ikmLen, salt, saltLen, info, 0); + HKDF256::ExtractExpand( + outputLamportSk, + HASH_LEN * 255, + ikm, + ikmLen, + salt, + saltLen, + info, + 0); } - static void ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { + static void ParentSkToLamportPK( + uint8_t* outputLamportPk, + const PrivateKey& parentSk, + uint32_t index) + { uint8_t* salt = Util::SecAlloc(4); uint8_t* ikm = Util::SecAlloc(HASH_LEN); uint8_t* notIkm = Util::SecAlloc(HASH_LEN); @@ -134,11 +108,15 @@ class HDKeys { uint8_t* lamportPk = Util::SecAlloc(HASH_LEN * 255 * 2); for (size_t i = 0; i < 255; i++) { - Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); + Util::Hash256( + lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); } - for (size_t i=0; i < 255; i++) { - Util::Hash256(lamportPk + 255 * HASH_LEN + i * HASH_LEN, lamport1 + i * HASH_LEN, HASH_LEN); + for (size_t i = 0; i < 255; i++) { + Util::Hash256( + lamportPk + 255 * HASH_LEN + i * HASH_LEN, + lamport1 + i * HASH_LEN, + HASH_LEN); } Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); @@ -150,7 +128,8 @@ class HDKeys { Util::SecFree(lamportPk); } - static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { + static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index) + { uint8_t* lamportPk = Util::SecAlloc(HASH_LEN); HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); @@ -159,21 +138,29 @@ class HDKeys { return child; } - static PrivateKey DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { + static PrivateKey DeriveChildSkUnhardened( + const PrivateKey& parentSk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); - memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); + memcpy( + buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); - PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); + PrivateKey ret = PrivateKey::Aggregate( + {parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); Util::SecFree(buf); Util::SecFree(digest); return ret; } - static G1Element DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { + static G1Element DeriveChildG1Unhardened( + const G1Element& pk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G1Element::SIZE); @@ -181,35 +168,33 @@ class HDKeys { Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); + blst_scalar nonce; + // The digest is big-endian; upstream chia loaded it as little-endian, + // which only worked because operator* reversed the bytes again. + blst_scalar_from_bendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); G1Element gen = G1Element::Generator(); - return pk + gen * nonce; + + return pk + (gen * nonce); } - static G2Element DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { + static G2Element DeriveChildG2Unhardened( + const G2Element& pk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G2Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G2Element::SIZE); Util::IntToFourBytes(buf + G2Element::SIZE, index); Util::Hash256(digest, buf, G2Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); + blst_scalar nonce; + // The digest is big-endian; upstream chia loaded it as little-endian, + // which only worked because operator* reversed the bytes again. + blst_scalar_from_bendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); @@ -218,5 +203,5 @@ class HDKeys { return pk + gen * nonce; } }; -} // end namespace bls +} // end namespace bls #endif // SRC_BLSHDKEYS_HPP_ diff --git a/include/dashbls/hkdf.hpp b/include/dashbls/hkdf.hpp index a7ddc863e..e338966dc 100644 --- a/include/dashbls/hkdf.hpp +++ b/include/dashbls/hkdf.hpp @@ -15,13 +15,7 @@ #ifndef SRC_BLSHKDF_HPP_ #define SRC_BLSHKDF_HPP_ -#include "relic_conf.h" #include - -#if defined GMP && ARITH == GMP -#include -#endif - #include #include "util.hpp" @@ -40,12 +34,11 @@ class HKDF256 { // assert(ikm_len == 32); // Used for EIP2333 key derivation // Hash256 used as the hash function (sha256) // PRK Output is 32 bytes (HashLen) - md_hmac(prk_output, ikm, ikm_len, salt, saltLen); + Util::md_hmac(prk_output, ikm, ikm_len, salt, saltLen); } static void Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen) { assert(L <= 255 * HASH_LEN); // L <= 255 * HashLen - assert(infoLen >= 0); size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up size_t bytesWritten = 0; @@ -59,12 +52,12 @@ class HKDF256 { if (i == 1) { memcpy(hmacInput1, info, infoLen); hmacInput1[infoLen] = i; - md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); + Util::md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); } else { memcpy(hmacInput, T, HASH_LEN); memcpy(hmacInput + HASH_LEN, info, infoLen); hmacInput[HASH_LEN + infoLen] = i; - md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); + Util::md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); } size_t to_write = L - bytesWritten; if (to_write > HASH_LEN) { diff --git a/include/dashbls/legacy.hpp b/include/dashbls/legacy.hpp index b3e1ee134..306217606 100644 --- a/include/dashbls/legacy.hpp +++ b/include/dashbls/legacy.hpp @@ -5,27 +5,24 @@ #ifndef SRC_LEGACY_HPP_ #define SRC_LEGACY_HPP_ -#include - -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - extern "C" { -#include "relic.h" +#include "blst.h" } +#include + +namespace bls { /** * Maps a byte array to a point in an elliptic curve over a quadratic extension. * - * Called ep2_map() in old relic version, _legacy prefix is to avoid duplicated symbols + * Called ep2_map() in old relic version. Now reimplemented on blst and must stay + * bit-for-bit compatible: pre-v19 (legacy scheme) consensus depends on it. * - * @param[out] p - the result. - * @param[in] msg - the byte array to map. - * @param[in] len - the array length in bytes. + * @param[out] p - the result. + * @param[in] msg - the byte array to map (must be 32 bytes). + * @param[in] len - the array length in bytes (must be 32). */ -void ep2_map_legacy(ep2_t p, const uint8_t *msg, int len); +void ep2_map_legacy(blst_p2 *p, const uint8_t *msg, size_t len); +} // namespace bls #endif // #define SRC_LEGACY_HPP_ diff --git a/include/dashbls/privatekey.hpp b/include/dashbls/privatekey.hpp index d02a7d292..a85051da5 100644 --- a/include/dashbls/privatekey.hpp +++ b/include/dashbls/privatekey.hpp @@ -15,12 +15,6 @@ #ifndef SRC_BLSPRIVATEKEY_HPP_ #define SRC_BLSPRIVATEKEY_HPP_ -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - #include "elements.hpp" namespace bls { @@ -34,9 +28,6 @@ class PrivateKey { // Construct a private key from a BIP32 based seed. static PrivateKey FromSeedBIP32(const Bytes& seed); - // Construct a random private key. - static PrivateKey RandomPrivateKey(); - // Construct a private key from a bytearray. static PrivateKey FromBytes(const Bytes& bytes, bool modOrder = false); @@ -76,13 +67,13 @@ class PrivateKey { friend G2Element operator*(const G2Element &a, const PrivateKey &k); friend G2Element operator*(const PrivateKey &k, const G2Element &a); - friend PrivateKey operator*(const PrivateKey& a, const bn_t& k); - friend PrivateKey operator*(const bn_t& k, const PrivateKey& a); + friend PrivateKey operator*(const PrivateKey& a, const blst_scalar& k); + friend PrivateKey operator*(const blst_scalar& k, const PrivateKey& a); // Serialize the key into bytes void Serialize(uint8_t *buffer) const; - std::vector Serialize(bool fLegacy = false) const; - std::array SerializeToArray(bool fLegacy = false) const; + std::vector Serialize() const; + std::array SerializeToArray() const; G2Element SignG2( const uint8_t *msg, @@ -105,7 +96,7 @@ class PrivateKey { void InvalidateCaches(); // The actual byte data - bn_st* keydata{nullptr}; + blst_scalar* keydata{nullptr}; mutable bool fG1CacheValid{false}; mutable G1Element g1Cache; diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index accc13b52..a7dbdf2b2 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -1,14 +1,14 @@ // Copyright 2020 Chia Network Inc // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in coiance with the License. +// you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or iied. +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. @@ -19,12 +19,6 @@ #include #include -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - #include "elements.hpp" #include "privatekey.hpp" @@ -36,7 +30,6 @@ namespace bls { class Bytes; class CoreMPL { - public: CoreMPL() = delete; CoreMPL(const std::string& strId) : strCiphersuiteId(strId) {} @@ -47,31 +40,42 @@ class CoreMPL { virtual PrivateKey KeyGen(const Bytes& seed); // Generates a public key from a secret key - virtual vector SkToPk(const PrivateKey &seckey); + virtual vector SkToPk(const PrivateKey& seckey); - virtual G1Element SkToG1(const PrivateKey &seckey); + virtual G1Element SkToG1(const PrivateKey& seckey); - virtual G2Element Sign(const PrivateKey &seckey, const vector &message); + virtual G2Element Sign( + const PrivateKey& seckey, + const vector& message); virtual G2Element Sign(const PrivateKey& seckey, const Bytes& message); - virtual bool Verify(const vector &pubkey, - const vector &message, - const vector &signature); + virtual bool Verify( + const vector& pubkey, + const vector& message, + const vector& signature); - virtual bool Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature); + virtual bool Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature); - virtual bool Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature); + virtual bool Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature); - virtual bool Verify(const G1Element& pubkey, const Bytes& message, const G2Element& signature); + virtual bool Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature); - virtual std::array Aggregate(const vector> &signatures); + virtual std::array Aggregate( + const vector>& signatures); virtual std::array Aggregate(const vector& signatures); - virtual G2Element Aggregate(const vector &signatures); + virtual G2Element Aggregate(const vector& signatures); - virtual G1Element Aggregate(const vector &publicKeys); + virtual G1Element Aggregate(const vector& publicKeys); virtual G2Element AggregateSecure(const std::vector& vecPublicKeys, const std::vector& vecSignatures, @@ -81,21 +85,25 @@ class CoreMPL { const G2Element& signature, const Bytes& message); - virtual bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature); + virtual bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature); - virtual bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature); - virtual bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature); - virtual bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature); PrivateKey DeriveChildSk(const PrivateKey& sk, uint32_t index); PrivateKey DeriveChildSkUnhardened(const PrivateKey& sk, uint32_t index); @@ -103,7 +111,8 @@ class CoreMPL { protected: const std::string& strCiphersuiteId; - bool NativeVerify(g1_t *pubKeys, g2_t *mappedHashes, size_t length); + static bool NativeVerify(const std::vector& g1s, + const std::vector& g2s); G2Element AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, const Bytes& message, @@ -118,74 +127,88 @@ class BasicSchemeMPL final : public CoreMPL { public: static const std::string CIPHERSUITE_ID; BasicSchemeMPL() : CoreMPL(BasicSchemeMPL::CIPHERSUITE_ID) {} - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) override; - - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) override; + bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) override; }; class AugSchemeMPL final : public CoreMPL { - public: static const std::string CIPHERSUITE_ID; AugSchemeMPL() : CoreMPL(AugSchemeMPL::CIPHERSUITE_ID) {} - G2Element Sign(const PrivateKey &seckey, const vector &message) override; + G2Element Sign(const PrivateKey& seckey, const vector& message) + override; G2Element Sign(const PrivateKey& seckey, const Bytes& message) override; // Used for prepending different augMessage - G2Element Sign(const PrivateKey &seckey, - const vector &message, - const G1Element &prepend_pk); + G2Element Sign( + const PrivateKey& seckey, + const vector& message, + const G1Element& prepend_pk); // Used for prepending different augMessage - G2Element Sign(const PrivateKey& seckey, - const Bytes& message, - const G1Element& prepend_pk); - - bool Verify(const vector &pubkey, - const vector &message, - const vector &signature) override; - - bool Verify(const Bytes& pubkey, - const Bytes& message, - const Bytes& signature) override; - - bool Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature) override; - - bool Verify(const G1Element& pubkey, - const Bytes& message, - const G2Element& signature) override; - - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) override; - - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) override; + G2Element Sign( + const PrivateKey& seckey, + const Bytes& message, + const G1Element& prepend_pk); + + bool Verify( + const vector& pubkey, + const vector& message, + const vector& signature) override; + + bool Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) override; + + bool Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature) override; + + bool Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) override; + + bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) override; bool VerifySecure(const std::vector& vecPublicKeys, const G2Element& signature, @@ -193,35 +216,38 @@ class AugSchemeMPL final : public CoreMPL { }; class PopSchemeMPL final : public CoreMPL { - public: static const std::string CIPHERSUITE_ID; static const std::string POP_CIPHERSUITE_ID; PopSchemeMPL() : CoreMPL(PopSchemeMPL::CIPHERSUITE_ID) {} - G2Element PopProve(const PrivateKey &seckey); + G2Element PopProve(const PrivateKey& seckey); - bool PopVerify(const G1Element &pubkey, const G2Element &signature_proof); + bool PopVerify(const G1Element& pubkey, const G2Element& signature_proof); - bool PopVerify(const vector &pubkey, const vector &proof); + bool PopVerify(const vector& pubkey, const vector& proof); bool PopVerify(const Bytes& pubkey, const Bytes& proof); - bool FastAggregateVerify(const vector &pubkeys, - const vector &message, - const G2Element &signature); - - bool FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const G2Element& signature); - - bool FastAggregateVerify(const vector> &pubkeys, - const vector &message, - const vector &signature); - - bool FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const Bytes& signature); + bool FastAggregateVerify( + const vector& pubkeys, + const vector& message, + const G2Element& signature); + + bool FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const G2Element& signature); + + bool FastAggregateVerify( + const vector>& pubkeys, + const vector& message, + const vector& signature); + + bool FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const Bytes& signature); }; /** @@ -232,23 +258,23 @@ class LegacySchemeMPL final : public CoreMPL { public: LegacySchemeMPL() : CoreMPL(std::string{}) {} - virtual vector SkToPk(const PrivateKey &seckey) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + virtual vector SkToPk(const PrivateKey &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - G2Element Sign(const PrivateKey &seckey, const vector &message) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + G2Element Sign(const PrivateKey &, const vector &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } G2Element Sign(const PrivateKey &seckey, const Bytes& message) final; - bool Verify(const vector& pubkey, - const vector& message, - const vector& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const vector&, + const vector&, + const vector&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool Verify(const G1Element& pubkey, - const vector& message, - const G2Element& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const G1Element&, + const vector&, + const G2Element&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const Bytes&, const Bytes&, const Bytes&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } bool Verify(const G1Element &pubkey, const Bytes& message, const G2Element &signature) final; - std::array Aggregate(const vector> &signatures) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + std::array Aggregate(const vector> &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } G2Element AggregateSecure(const std::vector& vecPublicKeys, const std::vector& vecSignatures, @@ -258,17 +284,17 @@ class LegacySchemeMPL final : public CoreMPL { const G2Element& signature, const Bytes& message) final; - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector> &, + const vector> &, + const vector &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool AggregateVerify(const vector &pubkeys, - const vector &messages, - const Bytes &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector &, + const vector &, + const Bytes &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector &, + const vector> &, + const G2Element &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } bool AggregateVerify(const vector &pubkeys, const vector &messages, diff --git a/include/dashbls/test-utils.hpp b/include/dashbls/test-utils.hpp index 154079ff4..237940c87 100644 --- a/include/dashbls/test-utils.hpp +++ b/include/dashbls/test-utils.hpp @@ -47,10 +47,10 @@ void endStopwatch(string testName, std::vector getRandomSeed() { uint8_t buf[32]; - bn_t r; - bn_new(r); - bn_rand(r, RLC_POS, 256); - bn_write_bin(buf, 32, r); + + for (int i = 0; i < 32; i++) + buf[i] = rand (); + std::vector ret(buf, buf + 32); return ret; } diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 4fa164bb2..02ec7b387 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -19,9 +19,14 @@ #include #include #include +#include #include #include +extern "C" { +#include "blst.h" +} + namespace bls { class BLS; @@ -60,8 +65,41 @@ class Util { public: static void Hash256(uint8_t* output, const uint8_t* message, size_t messageLen) { - md_map_sh256(output, message, messageLen); + blst_sha256(output, message, messageLen); + } + +static void md_hmac(uint8_t *mac, const uint8_t *in, size_t in_len, const uint8_t *key, + size_t key_len) { + constexpr size_t block_size = 64; + constexpr size_t md_len = 32; + // The pads and the padded key are derived from the key, so they live in + // the same secure memory as the private keys (see SecAlloc). + uint8_t *buf = SecAlloc(block_size + md_len + block_size + in_len + block_size); + if (buf == NULL) + throw std::runtime_error("out of memory"); + uint8_t *opad = buf; // block_size + md_len + uint8_t *ipad = opad + block_size + md_len; // block_size + in_len + uint8_t *_key = ipad + block_size + in_len; // block_size + + if (key_len > block_size) { + Hash256(_key, key, key_len); + key_len = md_len; + } else { + memcpy(_key, key, key_len); } + memset(_key + key_len, 0, block_size - key_len); + key = _key; + + for (size_t i = 0; i < block_size; i++) { + opad[i] = 0x5C ^ key[i]; + ipad[i] = 0x36 ^ key[i]; + } + memcpy(ipad + block_size, in, in_len); + Hash256(opad + block_size, ipad, block_size + in_len); + Hash256(mac, opad, block_size + md_len); + + SecFree(buf); +} static std::string HexStr(const uint8_t* data, size_t len) { std::stringstream s; diff --git a/js-bindings/CMakeLists.txt b/js-bindings/CMakeLists.txt index 5790f3a11..86660a01a 100644 --- a/js-bindings/CMakeLists.txt +++ b/js-bindings/CMakeLists.txt @@ -5,8 +5,6 @@ include_directories( ${INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include - ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include ${CMAKE_CURRENT_SOURCE_DIR}/../depends/catch2/include ) diff --git a/js-bindings/blsjs.d.ts b/js-bindings/blsjs.d.ts index 95cb05962..556a2a8ed 100644 --- a/js-bindings/blsjs.d.ts +++ b/js-bindings/blsjs.d.ts @@ -48,7 +48,6 @@ export declare class G1Element { deepcopy(): G1Element; getFingerprint(): number; add(el: G1Element): G1Element; - mul(bn: Bignum): G1Element; equalTo(el: G1Element): boolean; delete(): void; } @@ -63,7 +62,6 @@ export declare class G2Element { negate(): G2Element; deepcopy(): G2Element; add(el: G2Element): G2Element; - mul(bn: Bignum): G2Element; equalTo(el: G2Element): boolean; delete(): void; } @@ -82,12 +80,6 @@ export declare class PrivateKey { delete(): void; } -export declare class Bignum { - static fromString(s: string, radix: number): Bignum; - toString(radix: number): string; - delete(): void; -} - export declare class Util { static hash256(msg: Uint8Array): Uint8Array; static hexStr(msg: Uint8Array): string; @@ -100,7 +92,6 @@ export interface ModuleInstance { G1Element: typeof G1Element; G2Element: typeof G2Element; PrivateKey: typeof PrivateKey; - Bignum: typeof Bignum; Util: typeof Util; } diff --git a/js-bindings/helpers.cpp b/js-bindings/helpers.cpp index e58f5e490..41a92a0a2 100644 --- a/js-bindings/helpers.cpp +++ b/js-bindings/helpers.cpp @@ -26,12 +26,6 @@ namespace helpers { return buffer; } - val toUint8Array(bn_t bn) { - std::vector vec = toVector(bn); - val buffer = toUint8Array(vec); - return buffer; - } - std::vector toVector(uint8_t *pointer, size_t data_size) { std::vector data; data.reserve(data_size); @@ -48,13 +42,6 @@ namespace helpers { return vec; } - std::vector toVector(bn_t bn) { - uint8_t buf[bn_size_bin(bn)]; - bn_write_bin(buf, bn_size_bin(bn), bn); - std::vector vec = helpers::toVector(buf, bn_size_bin(bn)); - return vec; - } - std::vector> jsBuffersArrayToVector(val buffersArray) { auto l = buffersArray["length"].as(); std::vector> vec; @@ -64,20 +51,6 @@ namespace helpers { return vec; } - std::vector jsBuffersArrayToBnVector(val buffersArray) { - auto l = buffersArray["length"].as(); - std::vector vec; - for (unsigned i = 0; i < l; ++i) { - bn_t data; - bn_new(data); - std::vector bnVec = toVector(buffersArray[i]); - bn_read_bin(data, bnVec.data(), static_cast(bnVec.size())); - bn_t *point = &data; - vec.push_back(point); - } - return vec; - } - val byteArraysVectorToJsBuffersArray(std::vector arraysVector, size_t element_size) { auto vecSize = arraysVector.size(); std::vector valVector; diff --git a/js-bindings/helpers.h b/js-bindings/helpers.h index 031df0704..faf2976e3 100644 --- a/js-bindings/helpers.h +++ b/js-bindings/helpers.h @@ -18,9 +18,6 @@ #include #include #include -extern "C" { -#include -} #include using namespace emscripten; @@ -31,14 +28,10 @@ namespace helpers { val toUint8Array(std::vector vec); - val toUint8Array(bn_t bn); - std::vector toVector(uint8_t *pointer, size_t data_size); std::vector toVector(val jsBuffer); - std::vector toVector(bn_t bn); - template inline std::vector toVectorFromJSArray(val jsArray) { auto l = jsArray["length"].as(); @@ -62,8 +55,6 @@ namespace helpers { std::vector> jsBuffersArrayToVector(val buffersArray); - std::vector jsBuffersArrayToBnVector(val buffersArray); - val byteArraysVectorToJsBuffersArray(std::vector arraysVector, size_t element_size); } // namespace helpers diff --git a/js-bindings/jsbindings.cpp b/js-bindings/jsbindings.cpp index 81daa4abc..6e9d45f11 100644 --- a/js-bindings/jsbindings.cpp +++ b/js-bindings/jsbindings.cpp @@ -70,7 +70,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("deepcopy", &G1ElementWrapper::Deepcopy) .function("getFingerprint", &G1ElementWrapper::GetFingerprint) .function("add", &G1ElementWrapper::Add) - .function("mul", &G1ElementWrapper::Mul) .function("equalTo", &G1ElementWrapper::EqualTo); class_("G2Element") @@ -84,7 +83,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("negate", &G2ElementWrapper::Negate) .function("deepcopy", &G2ElementWrapper::Deepcopy) .function("add", &G2ElementWrapper::Add) - .function("mul", &G2ElementWrapper::Mul) .function("equalTo", &G2ElementWrapper::EqualTo); class_("PrivateKey") @@ -99,10 +97,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("mulG2", &PrivateKeyWrapper::MulG2) .function("equalTo", &PrivateKeyWrapper::EqualTo); - class_("Bignum") - .class_function("fromString", &BignumWrapper::FromString) - .function("toString", &BignumWrapper::ToString); - class_("Util") .class_function("hash256", &UtilWrapper::Hash256) .class_function("hexStr", &UtilWrapper::HexStr); diff --git a/js-bindings/tests/test.js b/js-bindings/tests/test.js index 34671d654..7c1455932 100644 --- a/js-bindings/tests/test.js +++ b/js-bindings/tests/test.js @@ -22,7 +22,6 @@ blsjs().then((blsjs) => { const modules = [ 'AugSchemeMPL', 'BasicSchemeMPL', - "Bignum", 'G1Element', 'G2Element', 'PopSchemeMPL', @@ -42,7 +41,6 @@ blsjs().then((blsjs) => { const { AugSchemeMPL, BasicSchemeMPL, - Bignum, G1Element, G2Element, PopSchemeMPL, @@ -380,17 +378,11 @@ blsjs().then((blsjs) => { assert(AugSchemeMPL.aggregateVerify([], [], new G2Element())); } - function testBignum() { - const mersenne = Bignum.fromString('162259276829213363391578010288127', 10); - assert(mersenne.toString(16).toLowerCase() == '7ffffffffffffffffffffffffff'); - } - testSchemes(); testVectorsInvalid(); testVectorsValid(); testReadme(); testAggregateVerifyZeroItems(); - testBignum(); }).then(function() { console.log("\nAll tests passed."); }); diff --git a/js-bindings/wrappers/BignumWrapper.cpp b/js-bindings/wrappers/BignumWrapper.cpp deleted file mode 100644 index 26fea069a..000000000 --- a/js-bindings/wrappers/BignumWrapper.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 Chia Network Inc - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include "BignumWrapper.h" - -namespace js_wrappers { -Bignum::Bignum() { - bn_make(&content, 1); -} - -Bignum::~Bignum() { - bn_clean(&content); -} - -Bignum::Bignum(const Bignum &other) { - bn_copy(&content, &other.content); -} - -bn_st *Bignum::operator &() { return &content; } - -BignumWrapper::BignumWrapper(const Bignum &bn) : JSWrapper(bn) {} - -BignumWrapper BignumWrapper::FromString(const std::string &s, int radix) { - Bignum result; - bn_read_str(&result, s.c_str(), s.size(), radix); - return BignumWrapper(result); -} - -std::string BignumWrapper::ToString(int radix) { - auto wrapper = GetWrappedInstance(); - int strsize = bn_size_str(&wrapper, radix); - std::vector strholder(strsize); - bn_write_str(&strholder[0], strsize, &wrapper, radix); - return std::string(&strholder[0], strsize - 1); -} -} // namespace js_wrappers diff --git a/js-bindings/wrappers/BignumWrapper.h b/js-bindings/wrappers/BignumWrapper.h deleted file mode 100644 index 337d52702..000000000 --- a/js-bindings/wrappers/BignumWrapper.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 Chia Network Inc - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef JS_BINDINGS_WRAPPERS_BIGNUMWRAPPER_H_ -#define JS_BINDINGS_WRAPPERS_BIGNUMWRAPPER_H_ - -#include -#include "JSWrapper.h" - -namespace js_wrappers { -class Bignum { -public: - Bignum(); - ~Bignum(); - Bignum(const Bignum &other); - - bn_st *operator &(); - -private: - bn_st content; -}; - -class BignumWrapper : public JSWrapper { -public: - explicit BignumWrapper(const Bignum &bn); - static BignumWrapper FromString(const std::string &s, int radix); - std::string ToString(int radix); -}; -} // namespace js_wrappers - -#endif // JS_BINDINGS_WRAPPERS_SIGNATUREWRAPPER_H_ diff --git a/js-bindings/wrappers/G1ElementWrapper.cpp b/js-bindings/wrappers/G1ElementWrapper.cpp index 0d6305148..f623a5062 100644 --- a/js-bindings/wrappers/G1ElementWrapper.cpp +++ b/js-bindings/wrappers/G1ElementWrapper.cpp @@ -44,16 +44,6 @@ G1ElementWrapper G1ElementWrapper::Add(const G1ElementWrapper &other) { return G1ElementWrapper(GetWrappedInstance() + other.GetWrappedInstance()); } -G1ElementWrapper G1ElementWrapper::Mul(const BignumWrapper &other) { - bn_t factor; - // Since the type of bn_t is bn_st[0], we can do this dance to make a temporary - // copy of the struct itself to hand to G1ElementWrapper's multiply. The - // type was clearly intended to by by-value in argument lists (degrade to ptr) - // but value semantics in C++ complicates matters. - factor[0] = *(&other.GetWrappedInstance()); - return G1ElementWrapper(GetWrappedInstance() * factor); -} - bool G1ElementWrapper::EqualTo(const G1ElementWrapper &others) { return GetWrappedInstance() == others.GetWrappedInstance(); } diff --git a/js-bindings/wrappers/G1ElementWrapper.h b/js-bindings/wrappers/G1ElementWrapper.h index 1b5a0f828..e7185c523 100644 --- a/js-bindings/wrappers/G1ElementWrapper.h +++ b/js-bindings/wrappers/G1ElementWrapper.h @@ -17,7 +17,6 @@ #include #include "JSWrapper.h" -#include "BignumWrapper.h" namespace js_wrappers { class G1ElementWrapper : public JSWrapper { @@ -38,8 +37,6 @@ class G1ElementWrapper : public JSWrapper { G1ElementWrapper Add(const G1ElementWrapper &other); - G1ElementWrapper Mul(const BignumWrapper &other); - bool EqualTo(const G1ElementWrapper &others); G1ElementWrapper Negate(); diff --git a/js-bindings/wrappers/G2ElementWrapper.cpp b/js-bindings/wrappers/G2ElementWrapper.cpp index 980a7dd8e..de4a208f5 100644 --- a/js-bindings/wrappers/G2ElementWrapper.cpp +++ b/js-bindings/wrappers/G2ElementWrapper.cpp @@ -67,16 +67,6 @@ G2ElementWrapper G2ElementWrapper::Add(const G2ElementWrapper& other) { return G2ElementWrapper(GetWrappedInstance() + other.GetWrappedInstance()); } -G2ElementWrapper G2ElementWrapper::Mul(const BignumWrapper& other) { - bn_t factor; - // Since the type of bn_t is bn_st[0], we can do this dance to make a - // temporary copy of the struct itself to hand to G2ElementWrapper's - // multiply. The type was clearly intended to by by-value in argument lists - // (degrade to ptr) but value semantics in C++ complicates matters. - factor[0] = *(&other.GetWrappedInstance()); - return G2ElementWrapper(GetWrappedInstance() * factor); -} - bool G2ElementWrapper::EqualTo(const G2ElementWrapper& others) { return GetWrappedInstance() == others.GetWrappedInstance(); } diff --git a/js-bindings/wrappers/G2ElementWrapper.h b/js-bindings/wrappers/G2ElementWrapper.h index 46f23f6ca..829affd2a 100644 --- a/js-bindings/wrappers/G2ElementWrapper.h +++ b/js-bindings/wrappers/G2ElementWrapper.h @@ -17,7 +17,6 @@ #include #include "JSWrapper.h" -#include "BignumWrapper.h" namespace js_wrappers { class G2ElementWrapper : public JSWrapper { @@ -46,8 +45,6 @@ class G2ElementWrapper : public JSWrapper { G2ElementWrapper Add(const G2ElementWrapper &other); - G2ElementWrapper Mul(const BignumWrapper &other); - bool EqualTo(const G2ElementWrapper &others); }; } // namespace js_wrappers diff --git a/pyproject.toml b/pyproject.toml index c9b5e5526..a427ccb8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,13 +47,9 @@ test-requires = [ ] [tool.cibuildwheel.linux] -before-all = "dnf -y install gmp-devel" manylinux-aarch64-image = "manylinux_2_28" manylinux-x86_64-image = "manylinux_2_28" -[tool.cibuildwheel.macos] -environment = { CMAKE_ARGS = "-DARITH=easy" } - [tool.pytest.ini_options] testpaths = ["binds/python"] addopts = "--benchmark-skip" diff --git a/rust-bindings/bls-dash-sys/build.rs b/rust-bindings/bls-dash-sys/build.rs index 7231004e2..c514df312 100644 --- a/rust-bindings/bls-dash-sys/build.rs +++ b/rust-bindings/bls-dash-sys/build.rs @@ -138,13 +138,9 @@ fn main() { .collect(); include_paths.extend([ - bls_dash_build_path.join("depends/relic-src/include"), - bls_dash_build_path.join("depends/relic/include"), bls_dash_build_path.join("src"), root_path.join("include/dashbls"), - bls_dash_build_path.join("depends/relic/include"), bls_dash_build_path.join("depends/mimalloc/include"), - root_path.join("depends/relic/include"), root_path.join("depends/mimalloc/include"), bls_dash_src_path.clone(), ]); @@ -186,13 +182,6 @@ fn main() { cc.compile("bls-dash-sys"); - println!( - "cargo:rustc-link-search={}", - root_path.join("build/depends/relic/lib").display() - ); - - println!("cargo:rustc-link-lib=static=relic_s"); - let mimalloc_dir = root_path.join("build/depends/mimalloc"); println!("cargo:rustc-link-search={}", mimalloc_dir.display()); let mimalloc_lib = if mimalloc_dir.join("libmimalloc-secure-debug.a").exists() { @@ -209,26 +198,6 @@ fn main() { println!("cargo:rustc-link-lib=static=dashbls"); - // Link GMP if exists - let gmp_libraries_file_path = bls_dash_build_path.join("gmp_libraries.txt"); - - if gmp_libraries_file_path.exists() { - let gmp_libraries_path = PathBuf::from( - fs::read_to_string(gmp_libraries_file_path) - .expect("should read gmp includes from file"), - ); - - let gmp_libraries_parent_path = gmp_libraries_path - .parent() - .expect("can't get gmp libraries parent dir"); - - println!( - "cargo:rustc-link-search={}", - gmp_libraries_parent_path.display() - ); - - println!("cargo:rustc-link-lib=static=gmp"); - } println!("cargo:warning=########## bls_dash_build_path:{}", bls_dash_build_path.display()); // Generate rust code for c binding to src/lib.rs @@ -395,12 +364,8 @@ fn main() { .collect(); include_paths.extend([ - bls_dash_build_path.join(format!("relic-{}-{}/depends/relic-src/include", platform, arch)), - bls_dash_build_path.join(format!("relic-{}-{}/depends/relic/include", platform, arch)), - bls_dash_build_path.join("contrib/relic/src"), root_path.join("src"), root_path.join("include/dashbls"), - root_path.join("depends/relic/include"), root_path.join("depends/mimalloc/include"), root_path.join("depends/catch2/include"), bls_dash_src_path.clone(), @@ -421,15 +386,10 @@ fn main() { .flag("-Wno-delete-non-abstract-non-virtual-dtor") .flag("-std=c++14"); - cc.compile("dashbls"); + cc.compile("bls-dash-sys"); println!("cargo:rustc-link-search={}", target_path.display()); - println!("cargo:rustc-link-lib=static=gmp"); - //println!("cargo:rustc-link-lib=c++"); - //println!("cargo:rustc-link-lib=c"); - println!("cargo:rustc-link-lib=static=relic_s"); - println!("cargo:rustc-link-lib=static=bls"); - println!("cargo:rustc-link-search={}", bls_dash_src_path.display()); + println!("cargo:rustc-link-lib=static=mimalloc-secure"); println!("cargo:rustc-link-lib=static=dashbls"); println!("cargo:rerun-if-changed={}", bls_dash_src_path.display()); } diff --git a/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp b/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp index bc2e632c8..96c0d2fa4 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp @@ -324,8 +324,13 @@ G2Element LegacySchemeMPLSign( { bls::LegacySchemeMPL* schemePtr = (bls::LegacySchemeMPL*)scheme; const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - return new bls::G2Element( - schemePtr->Sign(*skPtr, bls::Bytes((uint8_t*)msg, msgLen))); + try { + return new bls::G2Element( + schemePtr->Sign(*skPtr, bls::Bytes((uint8_t*)msg, msgLen))); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } bool LegacySchemeMPLVerify( diff --git a/rust-bindings/bls-signatures/src/schemes.rs b/rust-bindings/bls-signatures/src/schemes.rs index 3019bc0b4..1755bc354 100644 --- a/rust-bindings/bls-signatures/src/schemes.rs +++ b/rust-bindings/bls-signatures/src/schemes.rs @@ -1,9 +1,9 @@ -use std::ffi::c_void; +use std::ffi::{c_void, CStr}; use bls_dash_sys::{ AugSchemeMPLAggregateVerify, AugSchemeMPLFree, AugSchemeMPLSign, AugSchemeMPLVerify, BasicSchemeMPLAggregateVerify, BasicSchemeMPLFree, CoreMPLAggregatePubKeys, - CoreMPLAggregateSigs, CoreMPLSign, CoreMPLVerify, CoreMPLVerifySecure, + CoreMPLAggregateSigs, CoreMPLSign, CoreMPLVerify, CoreMPLVerifySecure, GetLastErrorMsg, LegacySchemeMPLAggregateVerify, LegacySchemeMPLSign, LegacySchemeMPLVerify, LegacySchemeMPLVerifySecure, NewAugSchemeMPL, NewBasicSchemeMPL, NewLegacySchemeMPL, }; @@ -198,16 +198,23 @@ impl Scheme for LegacySchemeMPL { } fn sign(&self, private_key: &PrivateKey, message: &[u8]) -> G2Element { - G2Element { - c_element: unsafe { - LegacySchemeMPLSign( - self.scheme, - private_key.as_mut_ptr(), - message.as_ptr() as *const _, - message.len(), - ) - }, + // The legacy scheme signs 32-byte message hashes; the C++ side throws + // for anything else, and a C++ exception must not cross the FFI. + assert_eq!(message.len(), 32, "the legacy scheme signs a 32-byte message hash"); + let c_element = unsafe { + LegacySchemeMPLSign( + self.scheme, + private_key.as_mut_ptr(), + message.as_ptr() as *const _, + message.len(), + ) + }; + // The C shim returns null when the C++ side threw. + if c_element.is_null() { + let msg = unsafe { CStr::from_ptr(GetLastErrorMsg()) }; + panic!("LegacySchemeMPLSign failed: {}", msg.to_string_lossy()); } + G2Element { c_element } } fn verify(&self, public_key: &G1Element, message: &[u8], signature: &G2Element) -> bool { @@ -383,10 +390,11 @@ mod tests { .g1_element() .expect("unable to get public key"); - let message_1 = b"ayya"; - let message_2 = b"ayyb"; - let message_3 = b"ayyc"; - let message_4 = b"ayyd"; + // 32-byte messages: the legacy scheme only signs message hashes + let message_1 = &[0x11u8; 32]; + let message_2 = &[0x22u8; 32]; + let message_3 = &[0x33u8; 32]; + let message_4 = &[0x44u8; 32]; let signature_1 = scheme.sign(&private_key_1, message_1); let signature_2 = scheme.sign(&private_key_2, message_2); @@ -431,4 +439,42 @@ mod tests { fn verify_aggregate_legacy() { verify_aggregate(LegacySchemeMPL::new()); } + + fn legacy_key_and_signature() -> (LegacySchemeMPL, G1Element, G2Element) { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + let public_key = private_key.g1_element().expect("unable to get public key"); + let signature = scheme.sign(&private_key, &[0x11u8; 32]); + (scheme, public_key, signature) + } + + #[test] + #[should_panic(expected = "32-byte message hash")] + fn legacy_sign_rejects_short_message() { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + scheme.sign(&private_key, &[0x11u8; 31]); + } + + #[test] + #[should_panic(expected = "32-byte message hash")] + fn legacy_sign_rejects_long_message() { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + scheme.sign(&private_key, &[0x11u8; 33]); + } + + #[test] + fn legacy_verify_rejects_wrong_message_length() { + let (scheme, public_key, signature) = legacy_key_and_signature(); + assert!(scheme.verify(&public_key, &[0x11u8; 32], &signature)); + for message in [&[0x11u8; 31][..], &[0x11u8; 33][..]] { + assert!(!scheme.verify(&public_key, message, &signature)); + assert!(!scheme.verify_secure([&public_key], message, &signature)); + assert!(!scheme.aggregate_verify([&public_key], [message], &signature)); + } + } } diff --git a/setup.py b/setup.py index cb8f9b6b8..6b748cfe5 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,6 @@ def build_extension(self, ext: CMakeExtension) -> None: "-DBUILD_BLS_BENCHMARKS=OFF", "-DBUILD_BLS_TESTS=OFF", "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, - "-DMULTI=", "-DPYBIND11_FINDPYTHON=ON", "-DPython_EXECUTABLE=" + sys.executable, "-DVERSION_INFO=" + self.distribution.get_version(), diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5802843a6..02eb9ae8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,41 +1,63 @@ -file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../include/dashbls/*.hpp) +file(GLOB HEADERS ${PROJECT_SOURCE_DIR}/include/dashbls/*.hpp) source_group("SrcHeaders" FILES ${HEADERS}) -add_library(dashbls +list(APPEND bls_sources ${HEADERS} - ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/bls.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/chaincode.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/elements.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/extendedprivatekey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/extendedpublickey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/legacy.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp) - -target_include_directories(dashbls - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - $<$:${GMP_INCLUDES}> - ${CMAKE_CURRENT_SOURCE_DIR}/../include/dashbls - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/mimalloc/include - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include - ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include + privatekey.cpp + bls.cpp + chaincode.cpp + elements.cpp + extendedprivatekey.cpp + extendedpublickey.cpp + legacy.cpp + schemes.cpp + threshold.cpp + ${blst_SOURCE_DIR}/src/server.c ) -target_compile_definitions(dashbls - PRIVATE - BLSALLOC_MIMALLOC=1 -) +if(MSVC) + list(APPEND bls_sources + ${blst_SOURCE_DIR}/build/win64/add_mod_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/add_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/add_mod_384x384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ct_inverse_mod_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ct_is_square_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ctq_inverse_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ctx_inverse_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/div3w-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulq_mont_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulq_mont_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulx_mont_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulx_mont_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/sha256-x86_64.asm + ) +else() + list(APPEND bls_sources + ${blst_SOURCE_DIR}/build/assembly.S + ) + set_source_files_properties(${blst_SOURCE_DIR}/src/server.c PROPERTIES COMPILE_OPTIONS -fno-builtin) + add_compile_options(-fPIC) + add_compile_options(-Wall) + add_compile_options(-Wextra) + if((CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") AND (NOT EMSCRIPTEN)) + add_compile_options(-mno-avx) + endif() +endif() +add_library(dashbls ${bls_sources}) +target_include_directories(dashbls PUBLIC ${blst_SOURCE_DIR}/bindings ${PROJECT_SOURCE_DIR}/include/dashbls/) +target_compile_definitions(dashbls PRIVATE __BLST_PORTABLE__ BLSALLOC_MIMALLOC=1) +if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64|aarch64|arm64|ARM64") + # blst ships assembly for x86_64 and aarch64 only; other 64-bit targets + # (riscv64, powerpc64, ...) build its portable C code instead. + target_compile_definitions(dashbls PRIVATE __BLST_NO_ASM__) +endif() target_link_libraries(dashbls PUBLIC - relic_s mimalloc-static ) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include/ DESTINATION include/dashbls) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include/ DESTINATION include/dashbls) +install(FILES ${blst_SOURCE_DIR}/bindings/blst.h ${blst_SOURCE_DIR}/bindings/blst_aux.h DESTINATION include/dashbls) install(FILES ${HEADERS} DESTINATION include/dashbls) install(FILES $ DESTINATION lib) @@ -43,9 +65,13 @@ if(BUILD_BLS_TESTS) include(${CMAKE_CURRENT_SOURCE_DIR}/../depends/catch2/CMakeLists.txt) add_executable(runtest test.cpp) + if(EMSCRIPTEN) + target_link_options(runtest PRIVATE "-sEXPORTED_FUNCTIONS=_malloc") + endif() target_link_libraries(runtest - PRIVATE dashbls - PRIVATE catch2) + PRIVATE + dashbls + catch2) if(EMSCRIPTEN) target_link_options(runtest PRIVATE "SHELL:-s MALLOC=emmalloc") @@ -66,3 +92,19 @@ if(BUILD_BLS_BENCHMARKS) endif() endif() endif() + +if(BUILD_BLS_TESTS) + # runtest compares the library against test-vectors/reference.txt; + # vectorgen regenerates that file from whichever library it is built against. + target_compile_definitions(runtest PRIVATE + DASHBLS_REFERENCE_VECTORS="${CMAKE_CURRENT_SOURCE_DIR}/../test-vectors/reference.txt") + + add_executable(vectorgen ${CMAKE_CURRENT_SOURCE_DIR}/../tools/vectorgen.cpp) + target_include_directories(vectorgen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(vectorgen PRIVATE dashbls) + + if(EMSCRIPTEN) + # mimalloc's Emscripten backend is built on emmalloc + target_link_options(vectorgen PRIVATE "SHELL:-s MALLOC=emmalloc") + endif() +endif() diff --git a/src/bls.cpp b/src/bls.cpp index d233afe97..19cce171f 100644 --- a/src/bls.cpp +++ b/src/bls.cpp @@ -27,36 +27,14 @@ bool BLSInitResult = BLS::Init(); Util::SecureAllocCallback Util::secureAllocCallback; Util::SecureFreeCallback Util::secureFreeCallback; -static void relic_core_initializer(void* ptr) -{ - core_init(); - if (err_get_code() != RLC_OK) { - throw std::runtime_error("core_init() failed"); - } - - const int r = ep_param_set_any_pairf(); - if (r != RLC_OK) { - throw std::runtime_error("ep_param_set_any_pairf() failed"); - } -} - bool BLS::Init() { - if (ALLOC != AUTO) { - throw std::runtime_error("Must have ALLOC == AUTO"); - } #if BLSALLOC_MIMALLOC SetSecureAllocator(mi_malloc, mi_free); #else SetSecureAllocator(malloc, free); #endif -#if MULTI != RELIC_NONE - core_set_thread_initializer(relic_core_initializer, nullptr); -#else - relic_core_initializer(nullptr); -#endif - return true; } @@ -68,18 +46,4 @@ void BLS::SetSecureAllocator( Util::secureFreeCallback = freeCb; } - -void BLS::CheckRelicErrors(bool should_throw) -{ - if (!core_get()) { - throw std::runtime_error("Library not initialized properly. Call BLS::Init()"); - } - if (core_get()->code != RLC_OK) { - core_get()->code = RLC_OK; - if (should_throw) { - throw std::invalid_argument("Relic library error"); - } - } -} - } // end namespace bls diff --git a/src/bn_helpers.hpp b/src/bn_helpers.hpp deleted file mode 100644 index 05175437e..000000000 --- a/src/bn_helpers.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2026 The Dash Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef SRC_BN_HELPERS_HPP_ -#define SRC_BN_HELPERS_HPP_ - -#include "relic_conf.h" - -extern "C" { -#include "relic.h" -} - -#include - -namespace bls { - -// RAII wrapper for dynamically allocated bn_t arrays. -// Ensures bn_free + delete[] runs even when exceptions are thrown. -// Tracks initialization count so partial construction cleans up correctly. -struct BnArrayGuard { - bn_t* data; - size_t count; - size_t initialized{0}; - - explicit BnArrayGuard(size_t n) : data(new bn_t[n]), count(n) { - for (size_t i = 0; i < count; i++) { - bn_new(data[i]); - initialized++; - } - } - - ~BnArrayGuard() { - for (size_t i = 0; i < initialized; i++) { - bn_free(data[i]); - } - delete[] data; - } - - bn_t& operator[](size_t i) { return data[i]; } - const bn_t& operator[](size_t i) const { return data[i]; } - - BnArrayGuard(const BnArrayGuard&) = delete; - BnArrayGuard& operator=(const BnArrayGuard&) = delete; -}; - -// RAII wrapper for a single bn_t value. -struct BnGuard { - bn_t val; - - BnGuard() { bn_new(val); } - ~BnGuard() { bn_free(val); } - - operator bn_t&() { return val; } - - BnGuard(const BnGuard&) = delete; - BnGuard& operator=(const BnGuard&) = delete; -}; - -} // namespace bls - -#endif // SRC_BN_HELPERS_HPP_ diff --git a/src/chaincode.cpp b/src/chaincode.cpp index efacdc95a..e5a720470 100644 --- a/src/chaincode.cpp +++ b/src/chaincode.cpp @@ -14,6 +14,8 @@ #include "bls.hpp" +#include + namespace bls { ChainCode ChainCode::FromBytes(const Bytes& bytes) { @@ -21,21 +23,15 @@ ChainCode ChainCode::FromBytes(const Bytes& bytes) { throw std::invalid_argument("ChainCode::FromBytes: Invalid size"); } ChainCode c = ChainCode(); - bn_new(c.chainCode); - bn_read_bin(c.chainCode, bytes.begin(), ChainCode::SIZE); + memcpy(c.chainCode.data(), bytes.begin(), ChainCode::SIZE); return c; } -ChainCode::ChainCode(const ChainCode &cc) { - uint8_t bytes[ChainCode::SIZE]; - cc.Serialize(bytes); - bn_new(chainCode); - bn_read_bin(chainCode, bytes, ChainCode::SIZE); -} +ChainCode::ChainCode(const ChainCode &cc) : chainCode(cc.chainCode) {} // Comparator implementation. bool operator==(ChainCode const &a, ChainCode const &b) { - return bn_cmp(a.chainCode, b.chainCode) == RLC_EQ; + return a.chainCode == b.chainCode; } bool operator!=(ChainCode const &a, ChainCode const &b) { @@ -49,7 +45,7 @@ std::ostream &operator<<(std::ostream &os, ChainCode const &s) { } void ChainCode::Serialize(uint8_t *buffer) const { - bn_write_bin(buffer, ChainCode::SIZE, chainCode); + memcpy(buffer, chainCode.data(), SIZE); } std::vector ChainCode::Serialize() const { diff --git a/src/elements.cpp b/src/elements.cpp index b0c747af8..3f77b4d54 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -12,16 +12,194 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include #include "bls.hpp" #include "legacy.hpp" +namespace { +// The legacy codec reproduces the byte-level behavior of the relic-based +// implementation exactly, including its handling of malformed input. relic +// was built without CHECK, so its internal errors did not throw: an +// out-of-range field element left the coordinate at zero, and a failed +// square root left y holding its pre-existing raw contents (0, or a raw +// limb 1 when the sign bit had been stored). All of it is consensus-visible +// through re-serialization and is pinned by test-vectors/reference.txt. + +// BLS12-381 base field prime p, big-endian. +static const uint8_t P_BE[48] = { + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, + 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, + 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, + 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab}; + +// (p - 1) / 2, big-endian. The legacy sign convention is y > (p-1)/2 +// ("larger" root), matching relic's ep_pck/ep2_pck for pairing-friendly +// curves. +static const uint8_t HALF_P_BE[48] = { + 0x0d, 0x00, 0x88, 0xf5, 0x1c, 0xbf, 0xf3, 0x4d, 0x25, 0x8d, 0xd3, 0xdb, + 0x21, 0xa5, 0xd6, 0x6b, 0xb2, 0x3b, 0xa5, 0xc2, 0x79, 0xc2, 0x89, 0x5f, + 0xb3, 0x98, 0x69, 0x50, 0x7b, 0x58, 0x7b, 0x12, 0x0f, 0x55, 0xff, 0xff, + 0x58, 0xa9, 0xff, 0xff, 0xdc, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xd5, 0x55}; + +// (2^384)^-1 mod p, big-endian. When relic (built without CHECK) failed to +// find a square root during legacy point decompression with the sign bit +// set, the y coordinate was left holding a raw limb value of 1, which reads +// back from Montgomery form as this constant. Preserved bit-for-bit for +// compatibility with historical consensus data. +static const uint8_t MONT_R_INV_BE[48] = { + 0x14, 0xfe, 0xc7, 0x01, 0xe8, 0xfb, 0x0c, 0xe9, 0xed, 0x5e, 0x64, 0x27, + 0x3c, 0x4f, 0x53, 0x8b, 0x17, 0x97, 0xab, 0x14, 0x58, 0xa8, 0x8d, 0xe9, + 0x34, 0x3e, 0xa9, 0x79, 0x14, 0x95, 0x6d, 0xc8, 0x7f, 0xe1, 0x12, 0x74, + 0xd8, 0x98, 0xfa, 0xfb, 0xf4, 0xd3, 0x82, 0x59, 0x38, 0x0b, 0x48, 0x20}; + +inline void FpFromBE48(blst_fp& out, const uint8_t* be) +{ + blst_fp_from_bendian(&out, be); +} + +// Compares a big-endian 48-byte value against a constant. Returns the +// memcmp sign. +inline int CmpBE48(const uint8_t* a, const uint8_t* b) +{ + return memcmp(a, b, 48); +} + +inline void FpZero(blst_fp& out) { memset(&out, 0x00, sizeof(blst_fp)); } + +inline void FpOneConst(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 1; + blst_fp_from_bendian(&out, be); +} + +// relic's ep2_upk sign rule (decompression side, with the IETF tie-break): +// sign = c1 > (p-1)/2, but when c1 == 0 the sign of c0 is used instead. +// Note the serialization side (ep2_pck) uses c1 only, with no tie-break. +inline bool Fp2SignUpk(const blst_fp2& y) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &y.fp[1]); + bool c1Zero = true; + for (int i = 0; i < 48; i++) + if (be[i] != 0) { c1Zero = false; break; } + if (!c1Zero) return CmpBE48(be, HALF_P_BE) > 0; + blst_bendian_from_fp(be, &y.fp[0]); + return CmpBE48(be, HALF_P_BE) > 0; +} + +// The "larger root" test used by both the legacy codec (relic's +// ep_pck/ep2_pck convention) and the legacy hash-to-curve: y > (p-1)/2 on +// the canonical representation. +inline bool FpSignGtHalf(const blst_fp& a) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &a); + return CmpBE48(be, HALF_P_BE) > 0; +} +} // anon namespace + namespace bls { +static void LegacyDecodeG1( + blst_p1_affine& a, + const uint8_t* bytes, + const bool signBit) +{ + uint8_t xbytes[G1Element::SIZE]; + memcpy(xbytes, bytes, G1Element::SIZE); + // the relic-based G1 codec erased the top three bits of the first byte + // unconditionally, before the sign bit was even examined (unlike G2, + // which only clears the sign bit) + xbytes[0] &= 0x1f; + + if (CmpBE48(xbytes, P_BE) >= 0) { + // relic's fp_read_bin error path: coordinate left untouched (zero) + FpZero(a.x); + } else { + FpFromBE48(a.x, xbytes); + } + + // rhs = x^3 + 4 + blst_fp rhs; + blst_fp_sqr(&rhs, &a.x); + blst_fp_mul(&rhs, &rhs, &a.x); + blst_fp four; + uint8_t fourBytes[48] = {0}; + fourBytes[47] = 4; + FpFromBE48(four, fourBytes); + blst_fp_add(&rhs, &rhs, &four); + + if (blst_fp_sqrt(&a.y, &rhs)) { + if (FpSignGtHalf(a.y) != signBit) { + blst_fp_cneg(&a.y, &a.y, true); + } + } else { + // relic's ep_upk failure: y keeps the raw sign-bit marker + if (signBit) { + FpFromBE48(a.y, MONT_R_INV_BE); + } else { + FpZero(a.y); + } + } +} + +static void LegacyDecodeG2( + blst_p2_affine& a, + const uint8_t* bytes, + const bool signBit) +{ + uint8_t c0[48], c1[48]; + memcpy(c0, bytes, 48); + memcpy(c1, bytes + 48, 48); + if (signBit) { + c0[0] &= 0x7f; + } + + if (CmpBE48(c0, P_BE) >= 0) { + FpZero(a.x.fp[0]); + } else { + FpFromBE48(a.x.fp[0], c0); + } + if (CmpBE48(c1, P_BE) >= 0) { + FpZero(a.x.fp[1]); + } else { + FpFromBE48(a.x.fp[1], c1); + } + + // rhs = x^3 + (4, 4) + blst_fp2 rhs; + blst_fp2_sqr(&rhs, &a.x); + blst_fp2_mul(&rhs, &rhs, &a.x); + blst_fp four; + uint8_t fourBytes[48] = {0}; + fourBytes[47] = 4; + FpFromBE48(four, fourBytes); + blst_fp2 b2; + b2.fp[0] = four; + b2.fp[1] = four; + blst_fp2_add(&rhs, &rhs, &b2); + + if (blst_fp2_sqrt(&a.y, &rhs)) { + if (Fp2SignUpk(a.y) != signBit) { + blst_fp2_cneg(&a.y, &a.y, true); + } + } else { + if (signBit) { + FpFromBE48(a.y.fp[0], MONT_R_INV_BE); + } else { + FpZero(a.y.fp[0]); + } + FpZero(a.y.fp[1]); + } +} const size_t G1Element::SIZE; -G1Element G1Element::FromBytes(Bytes const bytes, bool fLegacy) { +G1Element G1Element::FromBytes(Bytes const bytes, bool fLegacy) +{ G1Element ele = G1Element::FromBytesUnchecked(bytes, fLegacy); if (!fLegacy) { ele.CheckValid(); @@ -35,55 +213,46 @@ G1Element G1Element::FromBytesUnchecked(Bytes const bytes, bool fLegacy) throw std::invalid_argument("G1Element::FromBytes: Invalid size"); } - G1Element ele; - - // convert bytes to relic form - uint8_t buffer[G1Element::SIZE + 1]; - std::memcpy(buffer + 1, bytes.begin(), G1Element::SIZE); - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - + // check if the element is canonical + const uint8_t* raw_bytes = bytes.begin(); + bool fZerosOnly = (raw_bytes[0] & 0x1f) == 0 && + Util::HasOnlyZeros(Bytes(raw_bytes + 1, bytes.size() - 1)); if ((bytes[0] & 0xc0) == 0xc0) { // representing infinity - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G1Element::SIZE + 1)); // enforce that infinity must be 0xc0000..00 if (bytes[0] != 0xc0 || !fZerosOnly) { - throw std::invalid_argument("Given G1 infinity element must be canonical"); + throw std::invalid_argument( + "Given G1 infinity element must be canonical"); } + return G1Element(); // return infinity element (point all zero) + } + if (fLegacy) { + G1Element ele; + blst_p1_affine a; + LegacyDecodeG1(a, bytes.begin(), (bytes[0] & 0x80) != 0); + // Build the Jacobian point by hand: blst_p1_from_affine maps the + // all-zero affine pair to infinity, but relic kept degenerate + // decodes (e.g. x = 0, y = 0) as ordinary z=1 points, which is + // consensus-visible through re-serialization. + ele.p.x = a.x; + ele.p.y = a.y; + FpOneConst(ele.p.z); return ele; - } else { - if (fLegacy) { - if (bytes[0] & 0x80) { - buffer[0] = 0x03; // Insert extra byte for Y=1 - buffer[1] &= 0x7f; // Remove initial Y bit - } else { - buffer[0] = 0x02; // Insert extra byte for Y=0 - } - } else { - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G1Element::SIZE + 1)); - if ((bytes[0] & 0xc0) != 0x80) { - throw std::invalid_argument( - "Given G1 non-infinity element must start with 0b10"); - } - - if (fZerosOnly) { - throw std::invalid_argument("G1 non-infinity element can't have only zeros"); - } - if (bytes[0] & 0x20) { // sign bit - buffer[0] = 0x03; - } else { - buffer[0] = 0x02; - } - } } - g1_read_bin(ele.p, buffer, G1Element::SIZE + 1); - BLS::CheckRelicErrors(!fLegacy); - return ele; + if ((bytes[0] & 0xc0) != 0x80) { + throw std::invalid_argument( + "Given G1 non-infinity element must start with 0b10"); + } + if (fZerosOnly) { + throw std::invalid_argument( + "G1 non-infinity element can't have only zeros"); + } + + blst_p1_affine a; + BLST_ERROR err = blst_p1_uncompress(&a, bytes.begin()); + if (err != BLST_SUCCESS) + throw std::invalid_argument("G1Element::FromBytes: Invalid bytes"); + + return G1Element::FromAffine(a); } G1Element G1Element::FromByteVector(const std::vector& bytevec, bool fLegacy) @@ -91,35 +260,46 @@ G1Element G1Element::FromByteVector(const std::vector& bytevec, bool fL return G1Element::FromBytes(Bytes(bytevec), fLegacy); } -G1Element G1Element::FromNative(const g1_t element) +G1Element G1Element::FromNative(const blst_p1& element) { G1Element ele; - g1_copy(ele.p, element); + memcpy(&(ele.p), &element, sizeof(blst_p1)); return ele; } -G1Element G1Element::Copy() { +G1Element G1Element::FromAffine(const blst_p1_affine& element) +{ G1Element ele; - g1_copy(ele.p, this->p); - + blst_p1_from_affine(&(ele.p), &element); return ele; } - -G1Element G1Element::FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len) +G1Element G1Element::FromMessage( + const std::vector& message, + const uint8_t* dst, + size_t dst_len) { return FromMessage(Bytes(message), dst, dst_len); } -G1Element G1Element::FromMessage(Bytes const message, - const uint8_t* dst, - int dst_len) +G1Element G1Element::FromMessage( + Bytes const message, + const uint8_t* dst, + size_t dst_len) { G1Element ans; - ep_map_dst(ans.p, message.begin(), (int)message.size(), dst, dst_len); - BLS::CheckRelicErrors(); + const byte* aug = nullptr; + size_t aug_len = 0; + + blst_hash_to_g1( + &(ans.p), + message.begin(), + message.size(), + dst, + dst_len, + aug, + aug_len); + assert(ans.IsValid()); return ans; } @@ -127,35 +307,46 @@ G1Element G1Element::FromMessage(Bytes const message, G1Element G1Element::Generator() { G1Element ele; - g1_get_gen(ele.p); - BLS::CheckRelicErrors(); + ele.p = *(blst_p1_generator()); return ele; } -bool G1Element::IsValid() const { - // Infinity no longer valid in Relic - // https://github.com/relic-toolkit/relic/commit/f3be2babb955cf9f82743e0ae5ef265d3da6c02b - if (g1_is_infty((g1_st*)p) == 1) +bool G1Element::IsValid() const +{ + // Infinity was considered a valid G1Element in older Relic versions + // on which this library was previously based. + // For historical compatibililty this behavior is maintained. + + if (blst_p1_is_inf(&p)) return true; - return g1_is_valid((g1_st*)p); + // blst_p1_in_g1 assumes a point on the curve; relic's g1_is_valid + // checked both + return blst_p1_on_curve(&p) && blst_p1_in_g1(&p); } -void G1Element::CheckValid() const { +void G1Element::CheckValid() const +{ if (!IsValid()) throw std::invalid_argument("G1 element is invalid"); - BLS::CheckRelicErrors(); } -void G1Element::ToNative(g1_t output) const { - g1_copy(output, p); +void G1Element::ToNative(blst_p1* output) const +{ + memcpy(output, &p, sizeof(blst_p1)); } +void G1Element::ToAffine(blst_p1_affine* output) const +{ + blst_p1_to_affine(output, &p); +} + +G1Element G1Element::Copy() { return *this; } + G1Element G1Element::Negate() const { - G1Element ans; - g1_neg(ans.p, p); - BLS::CheckRelicErrors(); + G1Element ans = G1Element::FromNative(p); + blst_p1_cneg(&(ans.p), true); return ans; } @@ -165,84 +356,83 @@ uint32_t G1Element::GetFingerprint(const bool fLegacy) const { uint8_t buffer[G1Element::SIZE]; uint8_t hash[32]; - memcpy(buffer, Serialize(fLegacy).data(), G1Element::SIZE); + memcpy(buffer, SerializeToArray(fLegacy).data(), G1Element::SIZE); Util::Hash256(hash, buffer, G1Element::SIZE); return Util::FourBytesToInt(hash); } -std::vector G1Element::Serialize(const bool fLegacy) const { - const auto arr = G1Element::SerializeToArray(fLegacy); +std::vector G1Element::Serialize(const bool fLegacy) const +{ + const auto arr = SerializeToArray(fLegacy); return std::vector{arr.begin(), arr.end()}; } -std::array G1Element::SerializeToArray(const bool fLegacy) const { - uint8_t buffer[G1Element::SIZE + 1]; - g1_write_bin(buffer, G1Element::SIZE + 1, p, 1); - +std::array G1Element::SerializeToArray(const bool fLegacy) const +{ std::array result{}; - if (buffer[0] == 0x00) { // infinity - result[0] = 0xc0; + if (!fLegacy) { + blst_p1_compress(result.data(), &p); return result; } - if (buffer[0] == 0x03) { // sign bit set - buffer[1] |= fLegacy ? 0x80 : 0x20; + if (blst_p1_is_inf(&p)) { + result[0] = 0xc0; + return result; } - if (!fLegacy) { - buffer[1] |= 0x80; // indicate compression + blst_p1_affine a; + blst_p1_to_affine(&a, &p); + blst_bendian_from_fp(result.data(), &a.x); + if (FpSignGtHalf(a.y)) { + result[0] |= 0x80; } - - std::copy_n(buffer + 1, G1Element::SIZE, result.begin()); return result; } -bool operator==(const G1Element & a, const G1Element &b) +bool operator==(const G1Element& a, const G1Element& b) { - return g1_cmp(a.p, b.p) == RLC_EQ; + return blst_p1_is_equal(&(a.p), &(b.p)); } -bool operator!=(const G1Element & a, const G1Element & b) { return !(a == b); } +bool operator!=(const G1Element& a, const G1Element& b) { return !(a == b); } -std::ostream& operator<<(std::ostream& os, const G1Element &ele) +std::ostream& operator<<(std::ostream& os, const G1Element& ele) { return os << Util::HexStr(ele.Serialize()); } G1Element& operator+=(G1Element& a, const G1Element& b) { - g1_add(a.p, a.p, b.p); - BLS::CheckRelicErrors(); + blst_p1_add_or_double(&(a.p), &(a.p), &(b.p)); return a; } G1Element operator+(const G1Element& a, const G1Element& b) { G1Element ans; - g1_add(ans.p, a.p, b.p); - BLS::CheckRelicErrors(); + blst_p1_add_or_double(&(ans.p), &(a.p), &(b.p)); return ans; } -G1Element operator*(const G1Element& a, const bn_t& k) +G1Element operator*(const G1Element& a, const blst_scalar& k) { G1Element ans; - g1_mul(ans.p, (g1_st*)a.p, (bn_st*)k); - BLS::CheckRelicErrors(); + byte* bte = Util::SecAlloc(32); + blst_lendian_from_scalar(bte, &k); + blst_p1_mult(&(ans.p), &(a.p), bte, 256); + Util::SecFree(bte); + return ans; } -G1Element operator*(const bn_t& k, const G1Element& a) { return a * k; } - - +G1Element operator*(const blst_scalar& k, const G1Element& a) { return a * k; } // G2Element definitions below - - const size_t G2Element::SIZE; -G2Element G2Element::FromBytes(Bytes const bytes, const bool fLegacy) { +G2Element G2Element::FromBytes(Bytes const bytes, const bool fLegacy) +{ G2Element ele = G2Element::FromBytesUnchecked(bytes, fLegacy); if (!fLegacy) { ele.CheckValid(); @@ -256,59 +446,59 @@ G2Element G2Element::FromBytesUnchecked(Bytes const bytes, const bool fLegacy) throw std::invalid_argument("G2Element::FromBytes: Invalid size"); } - G2Element ele; - uint8_t buffer[G2Element::SIZE + 1]; - if (fLegacy) { - std::memcpy(buffer + 1, bytes.begin(), G2Element::SIZE); - buffer[0] = 0x00; - } else { - std::memcpy(buffer + 1, bytes.begin() + G2Element::SIZE / 2, G2Element::SIZE / 2); - std::memcpy(buffer + 1 + G2Element::SIZE / 2, bytes.begin(), G2Element::SIZE / 2); - buffer[0] = 0x00; - buffer[49] &= 0x1f; // erase 3 msbs from input - if ((bytes[48] & 0xe0) != 0x00) { + // The relic implementation computed its zeros-only check over a + // buffer that still contained the first byte unmasked, so for the + // legacy G2 codec any input with the two top bits set - canonical + // infinity included - was rejected as non-canonical. + if ((bytes[0] & 0xc0) == 0xc0) { throw std::invalid_argument( - "Given G2 element must always have 48th byte start with 0b000"); + "Given G2 infinity element must be canonical"); } + G2Element ele; + blst_p2_affine a; + LegacyDecodeG2(a, bytes.begin(), (bytes[0] & 0x80) != 0); + // See the G1 legacy path: keep degenerate all-zero decodes as + // ordinary z=1 points instead of mapping them to infinity. + ele.q.x = a.x; + ele.q.y = a.y; + FpOneConst(ele.q.z.fp[0]); + FpZero(ele.q.z.fp[1]); + return ele; + } + + if ((bytes[48] & 0xe0) != 0x00) { + throw std::invalid_argument( + "Given G2 element must always have 48th byte start with 0b000"); } - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G2Element::SIZE + 1)); - if (((bytes[0] & 0xc0) == 0xc0)) { // infinity + + bool fZerosOnly = (bytes[0] & 0x1f) == 0 && + Util::HasOnlyZeros(Bytes(bytes.begin() + 1, bytes.size() - 1)); + if ((bytes[0] & 0xc0) == 0xc0) { // infinity // enforce that infinity must be 0xc0000..00 if (bytes[0] != 0xc0 || !fZerosOnly) { throw std::invalid_argument( "Given G2 infinity element must be canonical"); } - return ele; + return G2Element(); } - if (fLegacy) { - if (bytes[0] & 0x80) { - buffer[0] = 0x03; // Insert extra byte for Y=1 - buffer[1] &= 0x7f; // Remove initial Y bit - } else { - buffer[0] = 0x02; // Insert extra byte for Y=0 - } - } else { - if (((bytes[0] & 0xc0) != 0x80)) { - throw std::invalid_argument( - "G2 non-inf element must have 0th byte start with 0b10"); - } - - if (fZerosOnly) { - throw std::invalid_argument("G2 non-infinity element can't have only zeros"); - } + if ((bytes[0] & 0xc0) != 0x80) { + throw std::invalid_argument( + "G2 non-inf element must have 0th byte start with 0b10"); + } - if (bytes[0] & 0x20) { - buffer[0] = 0x03; - } else { - buffer[0] = 0x02; - } + if (fZerosOnly) { + throw std::invalid_argument( + "G2 non-infinity element can't have only zeros"); } - g2_read_bin(ele.q, buffer, G2Element::SIZE + 1); - BLS::CheckRelicErrors(!fLegacy); - return ele; + blst_p2_affine a; + BLST_ERROR err = blst_p2_uncompress(&a, bytes.begin()); + if (err != BLST_SUCCESS) + throw std::invalid_argument("G2Element::FromBytes: Invalid bytes"); + + return G2Element::FromAffine(a); } G2Element G2Element::FromByteVector(const std::vector& bytevec, bool fLegacy) @@ -316,33 +506,51 @@ G2Element G2Element::FromByteVector(const std::vector& bytevec, bool fL return G2Element::FromBytes(Bytes(bytevec), fLegacy); } -G2Element G2Element::FromNative(const g2_t element) +G2Element G2Element::FromNative(const blst_p2& element) { G2Element ele; - g2_copy(ele.q, (g2_st*)element); + memcpy(&(ele.q), &element, sizeof(blst_p2)); return ele; } -G2Element G2Element::FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len, - const bool fLegacy) +G2Element G2Element::FromAffine(const blst_p2_affine& element) +{ + G2Element ele; + blst_p2_from_affine(&(ele.q), &element); + return ele; +} + +G2Element G2Element::FromMessage( + const std::vector& message, + const uint8_t* dst, + size_t dst_len, + const bool fLegacy) { return FromMessage(Bytes(message), dst, dst_len, fLegacy); } -G2Element G2Element::FromMessage(Bytes const message, - const uint8_t* dst, - int dst_len, - const bool fLegacy) +G2Element G2Element::FromMessage( + Bytes const message, + const uint8_t* dst, + size_t dst_len, + const bool fLegacy) { G2Element ans; if (fLegacy) { - ep2_map_legacy(ans.q, message.begin(), BLS::MESSAGE_HASH_LEN); + ep2_map_legacy(&(ans.q), message.begin(), message.size()); } else { - ep2_map_dst(ans.q, message.begin(), (int)message.size(), dst, dst_len); + const byte* aug = nullptr; + size_t aug_len = 0; + + blst_hash_to_g2( + &(ans.q), + message.begin(), + message.size(), + dst, + dst_len, + aug, + aug_len); } - BLS::CheckRelicErrors(); assert(ans.IsValid()); return ans; } @@ -350,139 +558,134 @@ G2Element G2Element::FromMessage(Bytes const message, G2Element G2Element::Generator() { G2Element ele; - g2_get_gen(ele.q); - BLS::CheckRelicErrors(); + ele.q = (*blst_p2_generator()); return ele; } -bool G2Element::IsValid() const { - // Infinity no longer valid in Relic - // https://github.com/relic-toolkit/relic/commit/f3be2babb955cf9f82743e0ae5ef265d3da6c02b - if (g2_is_infty((g2_st*)q) == 1) +bool G2Element::IsValid() const +{ + // Infinity was considered a valid G2Element in older Relic versions + // on which this library was previously based. + // For historical compatibililty this behavior is maintained. + + if (blst_p2_is_inf(&q)) return true; - return g2_is_valid((g2_st*)q); + return blst_p2_on_curve(&q) && blst_p2_in_g2(&q); } -void G2Element::CheckValid() const { +void G2Element::CheckValid() const +{ if (!IsValid()) throw std::invalid_argument("G2 element is invalid"); - BLS::CheckRelicErrors(); } -void G2Element::ToNative(g2_t output) const { - g2_copy(output, (g2_st*)q); +void G2Element::ToNative(blst_p2* output) const +{ + memcpy(output, (blst_p2*)&q, sizeof(blst_p2)); } -G2Element G2Element::Copy() { - G2Element ele; - g2_copy(ele.q, this->q); - - return ele; +void G2Element::ToAffine(blst_p2_affine* output) const +{ + blst_p2_to_affine(output, &q); } +G2Element G2Element::Copy() { return *this; } G2Element G2Element::Negate() const { - G2Element ans; - g2_neg(ans.q, (g2_st*)q); - BLS::CheckRelicErrors(); + G2Element ans = G2Element::FromNative(q); + blst_p2_cneg(&(ans.q), true); return ans; } GTElement G2Element::Pair(const G1Element& a) const { return a & (*this); } -std::vector G2Element::Serialize(const bool fLegacy) const { +std::vector G2Element::Serialize(const bool fLegacy) const +{ const auto arr = G2Element::SerializeToArray(fLegacy); return std::vector{arr.begin(), arr.end()}; } -std::array G2Element::SerializeToArray(const bool fLegacy) const { - uint8_t buffer[G2Element::SIZE + 1]; - g2_write_bin(buffer, G2Element::SIZE + 1, (g2_st*)q, 1); - +std::array G2Element::SerializeToArray(const bool fLegacy) const +{ std::array result{}; - - if (buffer[0] == 0x00) { // infinity - result.fill(0); - result[0] = 0xc0; + if (!fLegacy) { + blst_p2_compress(result.data(), &q); return result; } - if (fLegacy) { - if (buffer[0] == 0x03) { // sign bit set - buffer[1] |= 0x80; - } - } else { - // remove leading 3 bits - buffer[1] &= 0x1f; - buffer[49] &= 0x1f; - if (buffer[0] == 0x03) { - buffer[49] |= 0xa0; // swapped later to 0 - } else { - buffer[49] |= 0x80; - } + if (blst_p2_is_inf(&q)) { + result[0] = 0xc0; + return result; } - if (fLegacy) { - std::memcpy(result.data(), buffer + 1, G2Element::SIZE); - } else { - // Swap buffer, relic uses the opposite ordering for Fq2 elements - std::memcpy(result.data(), buffer + 1 + G2Element::SIZE / 2, G2Element::SIZE / 2); - std::memcpy(result.data() + G2Element::SIZE / 2, buffer + 1, G2Element::SIZE / 2); + blst_p2_affine a; + blst_p2_to_affine(&a, &q); + // legacy layout: x.c0 || x.c1 (the modern IETF layout is the swap) + blst_bendian_from_fp(result.data(), &a.x.fp[0]); + blst_bendian_from_fp(result.data() + 48, &a.x.fp[1]); + // legacy sign: c1 > (p-1)/2, no tie-break (relic's ep2_pck) + if (FpSignGtHalf(a.y.fp[1])) { + result[0] |= 0x80; } - return result; } bool operator==(G2Element const& a, G2Element const& b) { - return g2_cmp((g2_st*)a.q, (g2_st*)b.q) == RLC_EQ; + return blst_p2_is_equal(&(a.q), &(b.q)); } bool operator!=(G2Element const& a, G2Element const& b) { return !(a == b); } -std::ostream& operator<<(std::ostream& os, const G2Element & s) +std::ostream& operator<<(std::ostream& os, const G2Element& s) { return os << Util::HexStr(s.Serialize()); } G2Element& operator+=(G2Element& a, const G2Element& b) { - g2_add(a.q, a.q, (g2_st*)b.q); - BLS::CheckRelicErrors(); + blst_p2_add_or_double(&(a.q), &(a.q), &(b.q)); return a; } G2Element operator+(const G2Element& a, const G2Element& b) { G2Element ans; - g2_add(ans.q, (g2_st*)a.q, (g2_st*)b.q); - BLS::CheckRelicErrors(); + blst_p2_add_or_double(&(ans.q), &(a.q), &(b.q)); return ans; } -G2Element operator*(const G2Element& a, const bn_t& k) +G2Element operator*(const G2Element& a, const blst_scalar& k) { G2Element ans; - g2_mul(ans.q, (g2_st*)a.q, (bn_st*)k); - BLS::CheckRelicErrors(); + byte* bte = Util::SecAlloc(32); + blst_lendian_from_scalar(bte, &k); + blst_p2_mult(&(ans.q), &(a.q), bte, 256); + Util::SecFree(bte); + return ans; } -G2Element operator*(const bn_t& k, const G2Element& a) { return a * k; } - - +G2Element operator*(const blst_scalar& k, const G2Element& a) { return a * k; } // GTElement const size_t GTElement::SIZE; +/* + * Currently deserliazation is not available - these are currently + * broken and just return the zero element + */ GTElement GTElement::FromBytes(Bytes const bytes) { GTElement ele = GTElement::FromBytesUnchecked(bytes); - if (gt_is_valid(*(gt_t*)&ele) == 0) - throw std::invalid_argument("GTElement is invalid"); + // + // this doesn't seem to be the proper check as it doesn't work as expeced + // + // if (!blst_fp12_in_group(&(ele.r))) + // throw std::invalid_argument("GTElement is invalid"); return ele; } @@ -492,8 +695,7 @@ GTElement GTElement::FromBytesUnchecked(Bytes const bytes) throw std::invalid_argument("GTElement::FromBytes: Invalid size"); } GTElement ele = GTElement(); - gt_read_bin(ele.r, bytes.begin(), GTElement::SIZE); - BLS::CheckRelicErrors(); + memcpy(&(ele.r), bytes.begin(), SIZE); return ele; } @@ -502,23 +704,35 @@ GTElement GTElement::FromByteVector(const std::vector& bytevec) return GTElement::FromBytes(Bytes(bytevec)); } -GTElement GTElement::FromNative(const gt_t* element) +GTElement GTElement::FromNative(const blst_fp12* element) { GTElement ele = GTElement(); - gt_copy(ele.r, *(gt_t*)element); + ele.r = *element; return ele; } -GTElement GTElement::Unity() { +GTElement GTElement::FromAffine(const blst_p1_affine& affine) +{ GTElement ele = GTElement(); - gt_set_unity(ele.r); + blst_aggregated_in_g1(&ele.r, &affine); return ele; } +GTElement GTElement::FromAffine(const blst_p2_affine& affine) +{ + GTElement ele = GTElement(); + blst_aggregated_in_g2(&ele.r, &affine); + return ele; +} + +GTElement GTElement::Unity() +{ + return GTElement::FromNative(blst_fp12_one()); +} bool operator==(GTElement const& a, GTElement const& b) { - return gt_cmp(*(gt_t*)(a.r), *(gt_t*)(b.r)) == RLC_EQ; + return blst_fp12_is_equal(&(a.r), &(b.r)); } bool operator!=(GTElement const& a, GTElement const& b) { return !(a == b); } @@ -530,31 +744,31 @@ std::ostream& operator<<(std::ostream& os, GTElement const& ele) GTElement operator&(const G1Element& a, const G2Element& b) { - G1Element nonConstA(a); - gt_t ans; - gt_new(ans); - g2_t tmp; - g2_null(tmp); - g2_new(tmp); - b.ToNative(tmp); - pp_map_oatep_k12(ans, nonConstA.p, tmp); + blst_fp12 ans; + + blst_p1_affine aff1; + blst_p2_affine aff2; + a.ToAffine(&aff1); + b.ToAffine(&aff2); + + blst_miller_loop(&ans, &aff2, &aff1); + blst_final_exp(&ans, &ans); + GTElement ret = GTElement::FromNative(&ans); - gt_free(ans); - g2_free(tmp); + return ret; } GTElement operator*(GTElement& a, GTElement& b) { GTElement ans; - fp12_mul(ans.r, a.r, b.r); - BLS::CheckRelicErrors(); + blst_fp12_mul(&(ans.r), &(a.r), &(b.r)); return ans; } void GTElement::Serialize(uint8_t* buffer) const { - gt_write_bin(buffer, GTElement::SIZE, *(gt_t*)&r, 1); + memcpy(buffer, &r, GTElement::SIZE); } std::vector GTElement::Serialize() const diff --git a/src/extendedprivatekey.cpp b/src/extendedprivatekey.cpp index d5fe746ee..237aa0515 100644 --- a/src/extendedprivatekey.cpp +++ b/src/extendedprivatekey.cpp @@ -31,27 +31,16 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { // Hash the seed into 64 bytes, half will be sk, half will be cc hashInput[bytes.size()] = 0; - md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + Util::md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); hashInput[bytes.size()] = 1; - md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + Util::md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_t order; - bn_new(order); - g1_get_ord(order); - - bn_new(*skBn); - bn_read_bin(*skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); - bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, *skBn); - ExtendedPrivateKey esk(ExtendedPublicKey::REVISION, 0, 0, 0, ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), - PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); + PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true)); - Util::SecFree(skBn); Util::SecFree(ILeft); Util::SecFree(hashInput); return esk; @@ -101,13 +90,13 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega } hmacInput[inputLen - 1] = 0; - md_hmac(ILeft, hmacInput, inputLen, + Util::md_hmac(ILeft, hmacInput, inputLen, hmacKey, ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, + Util::md_hmac(IRight, hmacInput, inputLen, hmacKey, ChainCode::SIZE); PrivateKey newSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); diff --git a/src/extendedpublickey.cpp b/src/extendedpublickey.cpp index a484dea64..ad1015a68 100644 --- a/src/extendedpublickey.cpp +++ b/src/extendedpublickey.cpp @@ -59,13 +59,13 @@ ExtendedPublicKey ExtendedPublicKey::PublicChild(uint32_t i, const bool fLegacy) hmacInput[inputLen - 1] = 0; Util::IntToFourBytes(hmacInput + G1Element::SIZE, i); - md_hmac(ILeft, hmacInput, inputLen, + Util::md_hmac(ILeft, hmacInput, inputLen, hmacKey, ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, + Util::md_hmac(IRight, hmacInput, inputLen, hmacKey, ChainCode::SIZE); PrivateKey leftSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); diff --git a/src/legacy.cpp b/src/legacy.cpp index 44780528d..a7d5832b9 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -3,142 +3,69 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "legacy.hpp" - -#include - -#define B12_P381_S3 "BE32CE5FBEED9CA374D38C0ED41EEFD5BB675277CDF12D11BC2FB026C41400045C03FFFFFFFDFFFD" -#define B12_P381_S32 "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFE" - -/** - * Returns the (sqrt(-3) - 1) / 2 mod q in the curve, where q is the prime. - * - * @param[out] h - the returned cofactor. - */ -static void ep2_curve_get_s32(bn_t s32In) { - static std::once_flag is_initialized; - static bn_t s32; - std::call_once(is_initialized, [] { - bn_new(s32); - bn_read_str(s32, B12_P381_S32, sizeof(B12_P381_S32), 16); - }); - bn_copy(s32In, s32); +#include +#include +#include + +namespace { + +// sqrt(-3) mod p, big-endian, zero-padded to 48 bytes. Value taken from the +// relic implementation's B12_P381_S3 constant; validated by +// s3^2 == -3 (mod p). +const uint8_t S3_BE[48] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x32, 0xce, 0x5f, + 0xbe, 0xed, 0x9c, 0xa3, 0x74, 0xd3, 0x8c, 0x0e, 0xd4, 0x1e, 0xef, 0xd5, + 0xbb, 0x67, 0x52, 0x77, 0xcd, 0xf1, 0x2d, 0x11, 0xbc, 0x2f, 0xb0, 0x26, + 0xc4, 0x14, 0x00, 0x04, 0x5c, 0x03, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfd}; + +// (sqrt(-3) - 1) / 2 mod p (relic's B12_P381_S32). +const uint8_t S32_BE[48] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, + 0xdf, 0x76, 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, + 0xdd, 0xb3, 0xa9, 0x3b, 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, + 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe}; + +// Effective cofactor h_eff for BLS12-381 G2 (RFC 9380 section 8.8.2), +// big-endian, 80 bytes / 636 bits. Scalar multiplication by h_eff is +// equivalent to the psi-endomorphism-based cofactor clearing the relic +// implementation performed (verified empirically point-by-point). +const uint8_t H_EFF_BE[80] = { + 0x0b, 0xc6, 0x9f, 0x08, 0xf2, 0xee, 0x75, 0xb3, 0x58, 0x4c, 0x6a, 0x0e, + 0xa9, 0x1b, 0x35, 0x28, 0x88, 0xe2, 0xa8, 0xe9, 0x14, 0x5a, 0xd7, 0x68, + 0x99, 0x86, 0xff, 0x03, 0x15, 0x08, 0xff, 0xe1, 0x32, 0x9c, 0x2f, 0x17, + 0x87, 0x31, 0xdb, 0x95, 0x6d, 0x82, 0xbf, 0x01, 0x5d, 0x12, 0x12, 0xb0, + 0x2e, 0xc0, 0xec, 0x69, 0xd7, 0x47, 0x7c, 0x1a, 0xe9, 0x54, 0xcb, 0xc0, + 0x66, 0x89, 0xf6, 0xa3, 0x59, 0x89, 0x4c, 0x0a, 0xde, 0xbb, 0xf6, 0xb4, + 0xe8, 0x02, 0x00, 0x05, 0xaa, 0xa9, 0x55, 0x51}; + +void FpOne(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 1; + blst_fp_from_bendian(&out, be); } -/** - * Returns the sqrt(-3) mod q in the curve, where q is the prime. - * - * @param[out] h - the returned cofactor. - */ -static void ep2_curve_get_s3(bn_t s3In) { - static std::once_flag is_initialized; - static bn_t s3; - std::call_once(is_initialized, [] { - bn_new(s3); - bn_read_str(s3, B12_P381_S3, sizeof(B12_P381_S3), 16); - }); - bn_copy(s3In, s3); +void FpFour(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 4; + blst_fp_from_bendian(&out, be); } -/** - * Returns the 'b' coefficient of the currently configured elliptic curve. - * - * @param[out] b - the 'b' coefficient of the elliptic curve. - */ -static void ep2_curve_get_b(fp2_t b) { - ctx_t *ctx = core_get(); - fp_copy(b[0], ctx->ep2_b[0]); - fp_copy(b[1], ctx->ep2_b[1]); -} +inline void FpZero(blst_fp& out) { memset(&out, 0x00, sizeof(blst_fp)); } -/** - * Returns the variable used to parameterize the given prime modulus. - * - * @param[out] x - the integer parameter. - */ -static void fp_param_get_var(bn_t x) { - bn_t a; - - bn_null(a); - - RLC_TRY { - bn_new(a); - /* x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16). */ - bn_set_2b(x, 63); - bn_set_2b(a, 62); - bn_add(x, x, a); - bn_set_2b(a, 60); - bn_add(x, x, a); - bn_set_2b(a, 57); - bn_add(x, x, a); - bn_set_2b(a, 48); - bn_add(x, x, a); - bn_set_2b(a, 16); - bn_add(x, x, a); - bn_neg(x, x); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - bn_free(a); - } +bool FpIsZero(const blst_fp& a) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &a); + for (int i = 0; i < 48; i++) + if (be[i] != 0) return false; + return true; } -/** - * Multiplies a point by the cofactor in a Barreto-Lynn-Soctt. - * - * @param[out] r - the result. - * @param[in] p - the point to multiply. - */ -static void ep2_mul_cof_b12(ep2_t r, ep2_t p) { - bn_t x; - ep2_t t0, t1, t2, t3; - - ep2_null(t0); - ep2_null(t1); - ep2_null(t2); - ep2_null(t3); - bn_null(x); - - RLC_TRY { - ep2_new(t0); - ep2_new(t1); - ep2_new(t2); - ep2_new(t3); - bn_new(x); - - fp_param_get_var(x); - - /* Compute t0 = xP. */ - ep2_mul_basic(t0, p, x); - /* Compute t1 = [x^2]P. */ - ep2_mul_basic(t1, t0, x); - - /* t2 = (x^2 - x - 1)P = x^2P - x*P - P. */ - ep2_sub(t2, t1, t0); - ep2_sub(t2, t2, p); - /* t3 = \psi(x - 1)P. */ - ep2_sub(t3, t0, p); - ep2_norm(t3, t3); - ep2_frb(t3, t3, 1); - ep2_add(t2, t2, t3); - /* t3 = \psi^2(2P). */ - ep2_dbl(t3, p); - ep2_norm(t3, t3); - ep2_frb(t3, t3, 2); - ep2_add(t2, t2, t3); - ep2_norm(r, t2); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - ep2_free(t0); - ep2_free(t1); - ep2_free(t2); - ep2_free(t3); - bn_free(x); - } +bool Fp2IsZero(const blst_fp2& a) +{ + return FpIsZero(a.fp[0]) && FpIsZero(a.fp[1]); } /** @@ -146,288 +73,191 @@ static void ep2_mul_cof_b12(ep2_t r, ep2_t p) { * The algorithm is Shallue–van de Woestijne encoding from * Section 3 of "Indifferentiable Hashing to Barreto–Naehrig Curves" * from Fouque-Tibouchi: + * @param[out] p - the encoded point. + * @param[in] t - the field element to encode. + * Returns false when the result is the point at infinity (t == 0). */ -static void ep2_sw_encode(ep2_t p, fp2_t t) { - if (fp2_is_zero(t)) { +bool ep2_sw_encode(blst_p2_affine& p, const blst_fp2& t) +{ + if (Fp2IsZero(t)) { // Maps t=0 to the point at infinity. - ep2_set_infty(p); - return; + return false; } - fp2_t nt; // Negative t - fp2_t w; - fp2_t b; - bn_t s_n3; - bn_t s_n3m1o2; - fp2_t x1; - fp2_t x2; - fp2_t x3; - fp2_t rhs; - - fp2_new(nt); - fp2_new(w); - fp2_new(b); - bn_new(s_n3); - bn_new(s_n3m1o2); - fp2_new(x1); - fp2_new(x2); - fp2_new(x3); - fp2_new(rhs); - - // nt = -t - fp2_neg(nt, t); - - uint8_t buf0[RLC_FP_BYTES]; // t[1] - uint8_t buf1[RLC_FP_BYTES]; // -t[1] - fp_write_bin(buf0, RLC_FP_BYTES, t[1]); - fp_write_bin(buf1, RLC_FP_BYTES, nt[1]); - - // Compare bytes since fp_cmp compares montgomery representation - int parity = memcmp(buf0, buf1, RLC_FP_BYTES) > 0; + + // parity of t: compare the canonical bytes of t.c1 against those of + // (-t).c1 + blst_fp2 nt = t; + blst_fp2_cneg(&nt, &nt, true); + uint8_t buf0[48], buf1[48]; + blst_bendian_from_fp(buf0, &t.fp[1]); + blst_bendian_from_fp(buf1, &nt.fp[1]); + const bool parity = memcmp(buf0, buf1, 48) > 0; + + blst_fp one, four; + FpOne(one); + FpFour(four); + blst_fp2 b; + b.fp[0] = four; + b.fp[1] = four; // w = t^2 + b + 1 - fp2_mul(w, t, t); - ep2_curve_get_b(b); - fp2_add(w, w, b); - fp_add_dig(w[0], w[0], 1); + blst_fp2 w; + blst_fp2_sqr(&w, &t); + blst_fp2_add(&w, &w, &b); + blst_fp_add(&w.fp[0], &w.fp[0], &one); - if (fp2_is_zero(w)) { - ep2_curve_get_gen(p); + if (Fp2IsZero(w)) { + p = *blst_p2_affine_generator(); if (parity) { - ep2_neg(p, p); + blst_fp2_cneg(&p.y, &p.y, true); } - fp2_free(nt); - fp2_free(w); - fp2_free(b); - bn_free(s_n3); - bn_free(s_n3m1o2); - fp2_free(x1); - fp2_free(x2); - fp2_free(x3); - fp2_free(rhs); - return; + return true; } - // sqrt(-3) - ep2_curve_get_s3(s_n3); - fp2_t s_n3p; - fp2_null(s_n3p); - fp2_new(s_n3p); - fp2_t s_n3m1o2p; - fp2_null(s_n3m1o2p); - fp2_new(s_n3m1o2p); + blst_fp2 s3p, s32p; + blst_fp_from_bendian(&s3p.fp[0], S3_BE); + FpZero(s3p.fp[1]); + blst_fp_from_bendian(&s32p.fp[0], S32_BE); + FpZero(s32p.fp[1]); - fp2_zero(s_n3p); - fp2_zero(s_n3m1o2p); - fp_prime_conv(s_n3p[0], s_n3); + // w = sqrt(-3) * t / (t^2 + b + 1) + blst_fp2_inverse(&w, &w); + blst_fp2_mul(&w, &w, &s3p); + blst_fp2_mul(&w, &w, &t); - // (sqrt(-3) - 1) / 2 - ep2_curve_get_s32(s_n3m1o2); - fp_prime_conv(s_n3m1o2p[0], s_n3m1o2); - - fp2_inv(w, w); - fp2_mul(w, w, s_n3p); - fp2_mul(w, w, t); - - // x1 = -wt + sqrt(-3) - fp2_neg(x1, w); - fp2_mul(x1, x1, t); - fp2_add(x1, x1, s_n3m1o2p); + // x1 = -wt + (sqrt(-3) - 1) / 2 + blst_fp2 x1; + x1 = w; + blst_fp2_cneg(&x1, &x1, true); + blst_fp2_mul(&x1, &x1, &t); + blst_fp2_add(&x1, &x1, &s32p); // x2 = -x1 - 1 - fp2_neg(x2, x1); - fp_sub_dig(x2[0], x2[0], 1); + blst_fp2 x2 = x1; + blst_fp2_cneg(&x2, &x2, true); + blst_fp_sub(&x2.fp[0], &x2.fp[0], &one); // x3 = 1/w^2 + 1 - fp2_mul(x3, w, w); - fp2_inv(x3, x3); - fp_add_dig(x3[0], x3[0], 1); - - fp2_zero(p->y); - fp2_set_dig(p->z, 1); - - // if x1 has no y, try x2. if x2 has no y, try x3 - fp2_copy(p->x, x1); - - ep2_rhs(rhs, p); - int Xx1 = fp2_srt(p->y, rhs) ? 1 : -1; - fp2_copy(p->x, x2); - ep2_rhs(rhs, p); - int Xx2 = fp2_srt(p->y, rhs) ? 1 : -1; + blst_fp2 x3; + blst_fp2_sqr(&x3, &w); + blst_fp2_inverse(&x3, &x3); + blst_fp_add(&x3.fp[0], &x3.fp[0], &one); + + const auto rhs = [&b](blst_fp2& out, const blst_fp2& x) { + blst_fp2_sqr(&out, &x); + blst_fp2_mul(&out, &out, &x); + blst_fp2_add(&out, &out, &b); + }; + + blst_fp2 r, y; + rhs(r, x1); + const int Xx1 = blst_fp2_sqrt(&y, &r) ? 1 : -1; + rhs(r, x2); + const int Xx2 = blst_fp2_sqrt(&y, &r) ? 1 : -1; // This formula computes which index to use, in constant time // without conditional branches. It's taken from the paper. 3 is // added, because the % operator in c can be negative. - int index = ((((Xx1 - 1) * Xx2) % 3) + 3) % 3; - - if (index == 0) { - fp2_copy(p->x, x1); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } else if (index == 1) { - fp2_copy(p->x, x2); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } else if (index == 2) { - fp2_copy(p->x, x3); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } - - p->coord = 1; - - // Check for lexicografically greater than the negation - fp2_t ny; - fp2_null(ny); - fp2_new(ny); - fp2_neg(ny, p->y); - fp_write_bin(buf0, RLC_FP_BYTES, p->y[1]); - fp_write_bin(buf1, RLC_FP_BYTES, ny[1]); - - // Compare bytes since fp_cmp compares montgomery representation - if ((memcmp(buf0, buf1, RLC_FP_BYTES) > 0) != parity) { - ep2_neg(p, p); + const int index = ((((Xx1 - 1) * Xx2) % 3) + 3) % 3; + + p.x = (index == 0) ? x1 : (index == 1) ? x2 : x3; + rhs(r, p.x); + const bool haveRoot = blst_fp2_sqrt(&p.y, &r); + (void)haveRoot; + assert(haveRoot); // guaranteed by the SW construction + + // Normalize the sign of y against the parity of t + blst_fp2 ny = p.y; + blst_fp2_cneg(&ny, &ny, true); + blst_bendian_from_fp(buf0, &p.y.fp[1]); + blst_bendian_from_fp(buf1, &ny.fp[1]); + if ((memcmp(buf0, buf1, 48) > 0) != parity) { + p.y = ny; } + return true; +} - fp2_free(nt); - fp2_free(w); - fp2_free(b); - bn_free(s_n3); - bn_free(s_n3m1o2); - fp2_free(x1); - fp2_free(x2); - fp2_free(x3); - fp2_free(rhs); - - fp2_free(s_n3p); - fp2_free(s_n3m1o2p); - fp2_free(ny); +// Reduce a 64-byte big-endian value mod p into an fp element, matching +// relic's bn_read_bin + fp_prime_conv: split as hi*2^256 + lo with both +// halves < 2^256 < p. +void FpFromBE64Wide(blst_fp& out, const uint8_t* be64) +{ + uint8_t padded[48] = {0}; + blst_fp hi, lo, c256; + + memcpy(padded + 16, be64, 32); + blst_fp_from_bendian(&hi, padded); + memcpy(padded + 16, be64 + 32, 32); + blst_fp_from_bendian(&lo, padded); + + // 2^256 mod p == 2^256 (p is 381 bits) + memset(padded, 0, 48); + padded[15] = 0x01; + blst_fp_from_bendian(&c256, padded); + + blst_fp_mul(&out, &hi, &c256); + blst_fp_add(&out, &out, &lo); } +} // anon namespace + +namespace bls { +void ep2_map_legacy(blst_p2* p, const uint8_t* msg, size_t len) +{ + if (len != 32) { + throw std::invalid_argument( + "ep2_map_legacy requires a 32-byte message hash"); + } -void ep2_map_legacy(ep2_t p, const uint8_t *msg, int len) { - bn_t t00; - bn_t t01; - bn_t t10; - bn_t t11; - fp_t t00p; - fp_t t01p; - fp_t t10p; - fp_t t11p; - fp2_t t0p; - fp2_t t1p; - ep2_t p0; - ep2_t p1; - - bn_null(t00); - bn_null(t01); - bn_null(t10); - bn_null(t11); - fp_null(t00p); - fp_null(t01p); - fp_null(t10p); - fp_null(t11p); - fp2_null(t0p); - fp2_null(t1p); - ep2_null(p1); - ep2_null(p0); - - RLC_TRY { - uint8_t input[RLC_MD_LEN + 8]; - if (len != RLC_MD_LEN) { - RLC_THROW(ERR_CAUGHT); - } - memcpy(input, msg, len); - // b"G2_0_c0" - input[RLC_MD_LEN + 0] = 0x47; // G - input[RLC_MD_LEN + 1] = 0x32; // 2 - input[RLC_MD_LEN + 2] = 0x5f; // _ - input[RLC_MD_LEN + 3] = 0x30; // 0 - input[RLC_MD_LEN + 4] = 0x5f; // _ - input[RLC_MD_LEN + 5] = 0x63; // c - input[RLC_MD_LEN + 6] = 0x30; // 0 - - bn_new(t00); - bn_new(t01); - bn_new(t10); - bn_new(t11); - fp_new(t00p); - fp_new(t01p); - fp_new(t10p); - fp_new(t11p); - fp2_new(t0p); - fp2_new(t1p); - ep2_new(p1); - ep2_new(p0); - - uint8_t t00Bytes[RLC_MD_LEN * 2]; - uint8_t t01Bytes[RLC_MD_LEN * 2]; - uint8_t t10Bytes[RLC_MD_LEN * 2]; - uint8_t t11Bytes[RLC_MD_LEN * 2]; - - // b"G2_0_c0" - input[RLC_MD_LEN + 7] = 0x00; // 0 - md_map(t00Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t00Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_0_c1" - input[RLC_MD_LEN + 6] = 0x31; // b"1" - input[RLC_MD_LEN + 7] = 0; // 0 - - md_map(t01Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t01Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_1_c0" - input[RLC_MD_LEN + 3] = 0x31; // b"1" - input[RLC_MD_LEN + 6] = 0x30; // b"0" - input[RLC_MD_LEN + 7] = 0; // 0 - md_map(t10Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t10Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_1_c1" - input[RLC_MD_LEN + 6] = 0x31; // b"1" - input[RLC_MD_LEN + 7] = 0; // 0 - md_map(t11Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t11Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - bn_read_bin(t00, t00Bytes, RLC_MD_LEN * 2); - bn_read_bin(t01, t01Bytes, RLC_MD_LEN * 2); - bn_read_bin(t10, t10Bytes, RLC_MD_LEN * 2); - bn_read_bin(t11, t11Bytes, RLC_MD_LEN * 2); - - fp_prime_conv(t00p, t00); - fp_prime_conv(t01p, t01); - fp_prime_conv(t10p, t10); - fp_prime_conv(t11p, t11); - - fp_copy(t0p[0], t00p); - fp_copy(t0p[1], t01p); - fp_copy(t1p[0], t10p); - fp_copy(t1p[1], t11p); - - ep2_sw_encode(p0, t0p); - ep2_sw_encode(p1, t1p); - ep2_add(p0, p0, p1); - /* Now, multiply by cofactor to get the correct group. */ - ep2_mul_cof_b12(p, p0); + uint8_t input[32 + 8]; + memcpy(input, msg, 32); + + uint8_t t00Bytes[64], t01Bytes[64], t10Bytes[64], t11Bytes[64]; + + const auto draw = [&input](uint8_t* out, char i, char j) { + // tag: "G2__c" followed by a counter byte + input[32 + 0] = 0x47; // G + input[32 + 1] = 0x32; // 2 + input[32 + 2] = 0x5f; // _ + input[32 + 3] = (uint8_t)i; + input[32 + 4] = 0x5f; // _ + input[32 + 5] = 0x63; // c + input[32 + 6] = (uint8_t)j; + input[32 + 7] = 0; + blst_sha256(out, input, 32 + 8); + input[32 + 7] = 1; + blst_sha256(out + 32, input, 32 + 8); + }; + + draw(t00Bytes, '0', '0'); + draw(t01Bytes, '0', '1'); + draw(t10Bytes, '1', '0'); + draw(t11Bytes, '1', '1'); + + blst_fp2 t0, t1; + FpFromBE64Wide(t0.fp[0], t00Bytes); + FpFromBE64Wide(t0.fp[1], t01Bytes); + FpFromBE64Wide(t1.fp[0], t10Bytes); + FpFromBE64Wide(t1.fp[1], t11Bytes); + + blst_p2_affine a0, a1; + const bool has0 = ep2_sw_encode(a0, t0); + const bool has1 = ep2_sw_encode(a1, t1); + + blst_p2 sum; + memset(&sum, 0x00, sizeof(blst_p2)); // infinity + if (has0) { + blst_p2_from_affine(&sum, &a0); } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); + if (has1) { + blst_p2_add_or_double_affine(&sum, &sum, &a1); } - RLC_FINALLY { - fp2_free(t0p); - fp2_free(t1p); - fp_free(t00p); - fp_free(t01p); - fp_free(t10p); - fp_free(t11p); - bn_free(t00); - bn_free(t01); - bn_free(t10); - bn_free(t11); - ep2_free(p0); - ep2_free(p1); + + // Clear the cofactor by multiplying with h_eff (equivalent to the + // psi-based clearing the relic implementation used). + uint8_t heffLE[sizeof(H_EFF_BE)]; + for (size_t i = 0; i < sizeof(H_EFF_BE); i++) { + heffLE[i] = H_EFF_BE[sizeof(H_EFF_BE) - 1 - i]; } + blst_p2_mult(p, &sum, heffLE, 636); } +} // namespace bls diff --git a/src/privatekey.cpp b/src/privatekey.cpp index d07246d85..51eb8acd9 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + +#include + #include "bls.hpp" #include "legacy.hpp" @@ -28,81 +32,65 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { PrivateKey::PRIVATE_KEY_SIZE); // Hash the seed into sk - md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); - - bn_t order; - bn_new(order); - g1_get_ord(order); + Util::md_hmac(hash, seed.begin(), seed.size(), hmacKey, sizeof(hmacKey)); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, hash, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); - PrivateKey k; - bn_copy(k.keydata, *skBn); + blst_scalar_from_be_bytes(k.keydata, hash, PrivateKey::PRIVATE_KEY_SIZE); - Util::SecFree(skBn); Util::SecFree(hash); return k; } +// BLS12-381 group order r, big-endian. +// Taken from depends/blst/src/consts.c: BLS12_381_r = { 0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 } +// (little-endian limbs of the same number, with the comment z^4 - z^2 + 1, group order). + +static const uint8_t ORDER_BE[32] = { + 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, + 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}; + // Construct a private key from a bytearray. -PrivateKey PrivateKey::FromBytes(const Bytes& bytes, bool modOrder) +PrivateKey PrivateKey::FromBytes(const Bytes &bytes, bool modOrder) { if (bytes.size() != PRIVATE_KEY_SIZE) { throw std::invalid_argument("PrivateKey::FromBytes: Invalid size"); } PrivateKey k; - bn_read_bin(k.keydata, bytes.begin(), PrivateKey::PRIVATE_KEY_SIZE); - bn_t ord; - bn_new(ord); - g1_get_ord(ord); - if (modOrder) { - bn_mod_basic(k.keydata, k.keydata, ord); - } else { - if (bn_cmp(k.keydata, ord) > 0) { + if (modOrder) + // this allows any bytes to be input and does proper mod order + blst_scalar_from_be_bytes(k.keydata, bytes.begin(), bytes.size()); + else { + // Values strictly greater than the group order are rejected; the + // order itself is accepted, matching the relic-based + // implementation's bn_cmp(keydata, order) > 0 check. + if (memcmp(bytes.begin(), ORDER_BE, PRIVATE_KEY_SIZE) > 0) { throw std::invalid_argument( "PrivateKey byte data must be less than the group order"); } + blst_scalar_from_bendian(k.keydata, bytes.begin()); } return k; } // Construct a private key from a bytearray. -PrivateKey PrivateKey::FromByteVector(const std::vector bytes, bool modOrder) +PrivateKey PrivateKey::FromByteVector( + const std::vector bytes, + bool modOrder) { return PrivateKey::FromBytes(Bytes(bytes), modOrder); } -// Construct a private key from a bytearray. -PrivateKey PrivateKey::RandomPrivateKey() -{ - bn_t *r = Util::SecAlloc(1); - bn_new(*r); - bn_rand(*r, RLC_POS, 256); - PrivateKey k; - bn_copy(k.keydata, *r); - bn_t ord; - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(k.keydata, k.keydata, ord); - Util::SecFree(r); - return k; -} - -PrivateKey::PrivateKey() { - AllocateKeyData(); -}; +PrivateKey::PrivateKey() { AllocateKeyData(); }; // Construct a private key from another private key. PrivateKey::PrivateKey(const PrivateKey &privateKey) { privateKey.CheckKeyData(); AllocateKeyData(); - bn_copy(keydata, privateKey.keydata); + memcpy(keydata, privateKey.keydata, sizeof(blst_scalar)); } PrivateKey::PrivateKey(PrivateKey &&k) @@ -111,14 +99,11 @@ PrivateKey::PrivateKey(PrivateKey &&k) k.InvalidateCaches(); } -PrivateKey::~PrivateKey() -{ - DeallocateKeyData(); -} +PrivateKey::~PrivateKey() { DeallocateKeyData(); } void PrivateKey::DeallocateKeyData() { - if(keydata != nullptr) { + if (keydata != nullptr) { Util::SecFree(keydata); keydata = nullptr; } @@ -131,16 +116,16 @@ void PrivateKey::InvalidateCaches() fG2CacheValid = false; } -PrivateKey& PrivateKey::operator=(const PrivateKey& other) +PrivateKey &PrivateKey::operator=(const PrivateKey &other) { CheckKeyData(); other.CheckKeyData(); InvalidateCaches(); - bn_copy(keydata, other.keydata); + memcpy(keydata, other.keydata, sizeof(blst_scalar)); return *this; } -PrivateKey& PrivateKey::operator=(PrivateKey&& other) +PrivateKey &PrivateKey::operator=(PrivateKey &&other) { DeallocateKeyData(); keydata = std::exchange(other.keydata, nullptr); @@ -148,28 +133,28 @@ PrivateKey& PrivateKey::operator=(PrivateKey&& other) return *this; } -const G1Element& PrivateKey::GetG1Element() const +const G1Element &PrivateKey::GetG1Element() const { if (!fG1CacheValid) { CheckKeyData(); - g1_st *p = Util::SecAlloc(1); - g1_mul_gen(p, keydata); + blst_p1 *p = Util::SecAlloc(1); + blst_sk_to_pk_in_g1(p, keydata); - g1Cache = G1Element::FromNative(p); + g1Cache = G1Element::FromNative(*p); Util::SecFree(p); fG1CacheValid = true; } return g1Cache; } -const G2Element& PrivateKey::GetG2Element() const +const G2Element &PrivateKey::GetG2Element() const { if (!fG2CacheValid) { CheckKeyData(); - g2_st *q = Util::SecAlloc(1); - g2_mul_gen(q, keydata); + blst_p2 *q = Util::SecAlloc(1); + blst_sk_to_pk_in_g2(q, keydata); - g2Cache = G2Element::FromNative(q); + g2Cache = G2Element::FromNative(*q); Util::SecFree(q); fG2CacheValid = true; } @@ -184,11 +169,15 @@ bool PrivateKey::HasKeyData() const G1Element operator*(const G1Element &a, const PrivateKey &k) { k.CheckKeyData(); - g1_st* ans = Util::SecAlloc(1); + + blst_p1 *ans = Util::SecAlloc(1); a.ToNative(ans); - g1_mul(ans, ans, k.keydata); - G1Element ret = G1Element::FromNative(ans); + byte *bte = Util::SecAlloc(32); + blst_lendian_from_scalar(bte, k.keydata); + blst_p1_mult(ans, ans, bte, 256); + G1Element ret = G1Element::FromNative(*ans); Util::SecFree(ans); + Util::SecFree(bte); return ret; } @@ -197,40 +186,40 @@ G1Element operator*(const PrivateKey &k, const G1Element &a) { return a * k; } G2Element operator*(const G2Element &a, const PrivateKey &k) { k.CheckKeyData(); - g2_st* ans = Util::SecAlloc(1); + blst_p2 *ans = Util::SecAlloc(1); a.ToNative(ans); - g2_mul(ans, ans, k.keydata); - G2Element ret = G2Element::FromNative(ans); + byte *bte = Util::SecAlloc(32); + blst_lendian_from_scalar(bte, k.keydata); + blst_p2_mult(ans, ans, bte, 256); + G2Element ret = G2Element::FromNative(*ans); Util::SecFree(ans); + Util::SecFree(bte); return ret; } G2Element operator*(const PrivateKey &k, const G2Element &a) { return a * k; } -PrivateKey operator*(const PrivateKey& k, const bn_t& a) +PrivateKey operator*(const PrivateKey& k, const blst_scalar& a) { k.CheckKeyData(); - bn_t order; - bn_new(order); - g2_get_ord(order); - PrivateKey ret; - bn_mul_comba(ret.keydata, k.keydata, a); - bn_mod_basic(ret.keydata, ret.keydata, order); + blst_sk_mul_n_check(ret.keydata, k.keydata, &a); return ret; } -PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; } +PrivateKey operator*(const blst_scalar& a, const PrivateKey& k) { return k * a; } -G2Element PrivateKey::GetG2Power(const G2Element& element) const +G2Element PrivateKey::GetG2Power(const G2Element &element) const { CheckKeyData(); - g2_st* q = Util::SecAlloc(1); + blst_p2 *q = Util::SecAlloc(1); element.ToNative(q); - g2_mul(q, q, keydata); - - const G2Element ret = G2Element::FromNative(q); + byte *bte = Util::SecAlloc(32); + blst_lendian_from_scalar(bte, keydata); + blst_p2_mult(q, q, bte, 255); + const G2Element ret = G2Element::FromNative(*q); Util::SecFree(q); + Util::SecFree(bte); return ret; } @@ -240,30 +229,29 @@ PrivateKey PrivateKey::Aggregate(std::vector const &privateKeys) throw std::length_error("Number of private keys must be at least 1"); } - bn_t order; - bn_new(order); - g1_get_ord(order); - PrivateKey ret; assert(ret.IsZero()); for (size_t i = 0; i < privateKeys.size(); i++) { privateKeys[i].CheckKeyData(); - bn_add(ret.keydata, ret.keydata, privateKeys[i].keydata); - bn_mod_basic(ret.keydata, ret.keydata, order); + blst_sk_add_n_check(ret.keydata, ret.keydata, privateKeys[i].keydata); } return ret; } -bool PrivateKey::IsZero() const { +bool PrivateKey::IsZero() const +{ CheckKeyData(); - return bn_is_zero(keydata); + blst_scalar zro; + memset(&zro, 0x00, sizeof(blst_scalar)); + + return memcmp(keydata, &zro, sizeof(blst_scalar)) == 0; } bool operator==(const PrivateKey &a, const PrivateKey &b) { a.CheckKeyData(); b.CheckKeyData(); - return bn_cmp(a.keydata, b.keydata) == RLC_EQ; + return memcmp(a.keydata, b.keydata, sizeof(blst_scalar)) == 0; } bool operator!=(const PrivateKey &a, const PrivateKey &b) { return !(a == b); } @@ -274,17 +262,21 @@ void PrivateKey::Serialize(uint8_t *buffer) const throw std::runtime_error("PrivateKey::Serialize buffer invalid"); } CheckKeyData(); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, keydata); + // blst_bendian_from_scalar converts through a temporary limb vector and + // securely wipes it, which costs more than the serialization itself; + // the scalar's native bytes are little-endian, so copy and reverse. + blst_lendian_from_scalar(buffer, keydata); + std::reverse(buffer, buffer + PRIVATE_KEY_SIZE); } -std::vector PrivateKey::Serialize(const bool fLegacy) const +std::vector PrivateKey::Serialize() const { std::vector data(PRIVATE_KEY_SIZE); Serialize(data.data()); return data; } -std::array PrivateKey::SerializeToArray(bool fLegacy) const +std::array PrivateKey::SerializeToArray() const { std::array data{}; Serialize(data.data()); @@ -299,35 +291,34 @@ G2Element PrivateKey::SignG2( const bool fLegacy) const { CheckKeyData(); + if (fLegacy && len != BLS::MESSAGE_HASH_LEN) { + throw std::invalid_argument("legacy signing requires a 32-byte message hash"); + } - g2_t pt; - g2_null(pt); - g2_new(pt); - + blst_p2 *pt = Util::SecAlloc(1); if (fLegacy) { - ep2_map_legacy(pt, msg, BLS::MESSAGE_HASH_LEN); + ep2_map_legacy(pt, msg, len); } else { - ep2_map_dst(pt, msg, len, dst, dst_len); + blst_hash_to_g2(pt, msg, len, dst, dst_len, nullptr, 0); } - - g2_mul(pt, pt, keydata); - G2Element ret = G2Element::FromNative(pt); - g2_free(pt); + blst_sign_pk_in_g1(pt, pt, keydata); + G2Element ret = G2Element::FromNative(*pt); + Util::SecFree(pt); return ret; } void PrivateKey::AllocateKeyData() { assert(!keydata); - keydata = Util::SecAlloc(1); - keydata->alloc = RLC_BN_SIZE; - bn_zero(keydata); + keydata = Util::SecAlloc(1); + memset(keydata, 0x00, sizeof(blst_scalar)); } void PrivateKey::CheckKeyData() const { if (keydata == nullptr) { - throw std::runtime_error("PrivateKey::CheckKeyData keydata not initialized"); + throw std::runtime_error( + "PrivateKey::CheckKeyData keydata not initialized"); } } diff --git a/src/schemes.cpp b/src/schemes.cpp index 75cc7b372..506ea9eee 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "schemes.hpp" + +#include + #include #include #include @@ -19,9 +23,7 @@ #include "bls.hpp" #include "elements.hpp" -#include "schemes.hpp" #include "hdkeys.hpp" -#include "bn_helpers.hpp" using std::string; using std::vector; @@ -29,11 +31,8 @@ using std::vector; namespace bls { template -static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) +static void HashPubKeys(blst_scalar* computedTs, size_t nPubKeys, GetBytesFn getBytes) { - BnGuard order; - g2_get_ord(order.val); - std::vector vecBuffer(nPubKeys * G1Element::SIZE); for (size_t i = 0; i < nPubKeys; i++) { @@ -53,18 +52,19 @@ static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) std::memcpy(buffer + 4, pkHash, 32); Util::Hash256(hash, buffer, 4 + 32); - bn_read_bin(computedTs[i], hash, 32); - bn_mod_basic(computedTs[i], computedTs[i], order.val); + // reduces the hash mod the group order, like the former + // bn_read_bin + bn_mod + blst_scalar_from_be_bytes(&computedTs[i], hash, 32); } } -enum InvariantResult { BAD=false, GOOD=true, CONTINUE }; +enum InvariantResult { BAD = false, GOOD = true, CONTINUE }; // Enforce argument invariants for Agg Sig Verification InvariantResult VerifyAggregateSignatureArguments( const size_t nPubKeys, const size_t nMessages, - const G2Element &signature) + const G2Element& signature) { if (nPubKeys == 0) { return (nMessages == 0 && signature == G2Element() ? GOOD : BAD); @@ -78,47 +78,60 @@ InvariantResult VerifyAggregateSignatureArguments( /* These are all for the min-pubkey-size variant. TODO : analogs for min-signature-size */ -const std::string BasicSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; -const std::string AugSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; -const std::string PopSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; -const std::string PopSchemeMPL::POP_CIPHERSUITE_ID = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +const std::string BasicSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; +const std::string AugSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; +const std::string PopSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +const std::string PopSchemeMPL::POP_CIPHERSUITE_ID = + "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; -PrivateKey CoreMPL::KeyGen(const vector& seed) { +PrivateKey CoreMPL::KeyGen(const vector& seed) +{ return HDKeys::KeyGen(seed); } -PrivateKey CoreMPL::KeyGen(const Bytes& seed) { - return HDKeys::KeyGen(seed); -} +PrivateKey CoreMPL::KeyGen(const Bytes& seed) { return HDKeys::KeyGen(seed); } -vector CoreMPL::SkToPk(const PrivateKey &seckey) +vector CoreMPL::SkToPk(const PrivateKey& seckey) { return seckey.GetG1Element().Serialize(); } -G1Element CoreMPL::SkToG1(const PrivateKey &seckey) +G1Element CoreMPL::SkToG1(const PrivateKey& seckey) { return seckey.GetG1Element(); } -G2Element CoreMPL::Sign(const PrivateKey &seckey, const vector &message) +G2Element CoreMPL::Sign( + const PrivateKey& seckey, + const vector& message) { return CoreMPL::Sign(seckey, Bytes(message)); } G2Element CoreMPL::Sign(const PrivateKey& seckey, const Bytes& message) { - return seckey.SignG2(message.begin(), message.size(), (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()); + return seckey.SignG2( + message.begin(), + message.size(), + (const uint8_t*)strCiphersuiteId.c_str(), + strCiphersuiteId.length()); } -bool CoreMPL::Verify(const vector &pubkey, - const vector &message, // unhashed - const vector &signature) +bool CoreMPL::Verify( + const vector& pubkey, + const vector& message, // unhashed + const vector& signature) { return CoreMPL::Verify(Bytes(pubkey), Bytes(message), Bytes(signature)); } -bool CoreMPL::Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature) +bool CoreMPL::Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) { G1Element pubkeyElement; G2Element signatureElement; @@ -131,32 +144,33 @@ bool CoreMPL::Verify(const Bytes& pubkey, const Bytes& message, const Bytes& sig return CoreMPL::Verify(pubkeyElement, message, signatureElement); } -bool CoreMPL::Verify(const G1Element &pubkey, - const vector &message, // unhashed - const G2Element &signature) +bool CoreMPL::Verify( + const G1Element& pubkey, + const vector& message, // unhashed + const G2Element& signature) { return CoreMPL::Verify(pubkey, Bytes(message), signature); } -bool CoreMPL::Verify(const G1Element& pubkey, const Bytes& message, const G2Element& signature) +bool CoreMPL::Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) { + // Keep the relic-era semantics rather than blst_core_verify: elements are + // gated by IsValid() and the check is the product of pairings. const G2Element hashedPoint = G2Element::FromMessage(message, (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()); - std::array g1s; - std::array g2s; - - G1Element::Generator().Negate().ToNative(g1s[0]); if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature.IsValid() || signature == G2Element()) { return false; } - pubkey.ToNative(g1s[1]); - signature.ToNative(g2s[0]); - hashedPoint.ToNative(g2s[1]); - return CoreMPL::NativeVerify(g1s.data(), g2s.data(), 2); + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature, hashedPoint}; + return CoreMPL::NativeVerify(g1s, g2s); } std::array CoreMPL::Aggregate(const vector> &signatures) @@ -179,7 +193,7 @@ std::array CoreMPL::Aggregate(const vector& sig return CoreMPL::Aggregate(elements).SerializeToArray(); } -G2Element CoreMPL::Aggregate(const vector &signatures) +G2Element CoreMPL::Aggregate(const vector& signatures) { G2Element aggregated; for (const G2Element& signature : signatures) { @@ -188,7 +202,7 @@ G2Element CoreMPL::Aggregate(const vector &signatures) return aggregated; } -G1Element CoreMPL::Aggregate(const vector &publicKeys) +G1Element CoreMPL::Aggregate(const vector& publicKeys) { G1Element aggregated; for (const G1Element& publicKey : publicKeys) { @@ -199,13 +213,13 @@ G1Element CoreMPL::Aggregate(const vector &publicKeys) G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, - const Bytes& message, + const Bytes& /*message*/, const bool fLegacy) { if (vecSignatures.size() != vecPublicKeys.size()) { throw std::invalid_argument("LegacySchemeMPL::AggregateSigs sigs.size() != pubKeys.size()"); } - BnArrayGuard computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector, const G2Element*>> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = std::make_pair(vecPublicKeys[i].SerializeToArray(fLegacy), &vecSignatures[i]); @@ -214,7 +228,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, return std::memcmp(a.first.data(), b.first.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].first.data(); }); // Raise all signatures to power of the corresponding t's and aggregate the results into aggSig @@ -247,7 +261,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BnArrayGuard computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(fLegacy); @@ -256,7 +270,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].data(); }); G1Element publicKey; @@ -274,18 +288,21 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return CoreMPL::VerifySecure(vecPublicKeys, signature, message, false); } -bool CoreMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, // unhashed - const vector &signature) +bool CoreMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, // unhashed + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); const std::vector vecMessagesBytes(messages.begin(), messages.end()); - return CoreMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return CoreMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool CoreMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, // unhashed - const Bytes& signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, // unhashed + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -294,7 +311,8 @@ bool CoreMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); + const auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); if (arg_check != CONTINUE) { return arg_check; } @@ -311,92 +329,125 @@ bool CoreMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeyElements, messages, signatureElement); } -bool CoreMPL::AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { - return CoreMPL::AggregateVerify(pubkeys, std::vector(messages.begin(), messages.end()), signature); + return CoreMPL::AggregateVerify( + pubkeys, + std::vector(messages.begin(), messages.end()), + signature); } -bool CoreMPL::AggregateVerify(const vector& pubkeys, - const vector &messages, - const G2Element& signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToNative(&vecG1[0]); if (!signature.IsValid() || signature == G2Element()) { return false; } - signature.ToNative(&vecG2[0]); + std::vector vecG1; + std::vector vecG2; + vecG1.reserve(nPubKeys + 1); + vecG2.reserve(nPubKeys + 1); + vecG1.push_back(G1Element::Generator().Negate()); + vecG2.push_back(signature); for (size_t i = 0; i < nPubKeys; ++i) { if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { return false; } - pubkeys[i].ToNative(&vecG1[i + 1]); - G2Element::FromMessage(messages[i], (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()).ToNative(&vecG2[i + 1]); + vecG1.push_back(pubkeys[i]); + vecG2.push_back(G2Element::FromMessage(messages[i], (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length())); } - return CoreMPL::NativeVerify((g1_t*)vecG1.data(), (g2_t*)vecG2.data(), nPubKeys + 1); + return CoreMPL::NativeVerify(vecG1, vecG2); } -bool CoreMPL::NativeVerify(g1_t *pubkeys, g2_t *mappedHashes, size_t length) +bool CoreMPL::NativeVerify(const std::vector& g1s, + const std::vector& g2s) { - gt_t target, candidate, tmpPairing; - fp12_zero(target); - fp_set_dig(target[0][0][0], 1); - fp12_zero(candidate); - fp_set_dig(candidate[0][0][0], 1); - - // prod e(pubkey[i], hash[i]) * e(-g1, aggSig) - // Performs pubKeys.size() pairings, 250 at a time - - for (size_t i = 0; i < length; i += 250) { - size_t numPairings = std::min((length - i), (size_t)250); - pc_map_sim(tmpPairing, pubkeys + i, mappedHashes + i, numPairings); - fp12_mul(candidate, candidate, tmpPairing); - } - - // 1 =? prod e(pubkey[i], hash[i]) * e(-g1, aggSig) - if (gt_cmp(target, candidate) != RLC_EQ || core_get()->code != RLC_OK) { - core_get()->code = RLC_OK; + // 1 =? prod e(g1s[i], g2s[i]) + // A pairing with the point at infinity on either side contributes the + // identity, matching relic's pc_map_sim. + if (g1s.size() != g2s.size()) { return false; } - BLS::CheckRelicErrors(); - return true; + std::vector ps; + std::vector qs; + ps.reserve(g1s.size()); + qs.reserve(g2s.size()); + for (size_t i = 0; i < g1s.size(); ++i) { + blst_p1 p; + blst_p2 q; + g1s[i].ToNative(&p); + g2s[i].ToNative(&q); + // Infinity is decided on the projective points: blst's affine form + // cannot tell (0, 0) apart from infinity. + if (blst_p1_is_inf(&p) || blst_p2_is_inf(&q)) { + continue; + } + blst_p1_to_affine(&ps.emplace_back(), &p); + blst_p2_to_affine(&qs.emplace_back(), &q); + } + + blst_fp12 candidate = *blst_fp12_one(); + if (!ps.empty()) { + // One shared Miller loop for all pairs, like relic's pc_map_sim. + std::vector pPtrs(ps.size()); + std::vector qPtrs(qs.size()); + for (size_t i = 0; i < ps.size(); ++i) { + pPtrs[i] = &ps[i]; + qPtrs[i] = &qs[i]; + } + blst_miller_loop_n(&candidate, qPtrs.data(), pPtrs.data(), ps.size()); + } + + blst_final_exp(&candidate, &candidate); + return blst_fp12_is_one(&candidate); } -PrivateKey CoreMPL::DeriveChildSk(const PrivateKey& sk, uint32_t index) { +PrivateKey CoreMPL::DeriveChildSk(const PrivateKey& sk, uint32_t index) +{ return HDKeys::DeriveChildSk(sk, index); } -PrivateKey CoreMPL::DeriveChildSkUnhardened(const PrivateKey& sk, uint32_t index) { +PrivateKey CoreMPL::DeriveChildSkUnhardened( + const PrivateKey& sk, + uint32_t index) +{ return HDKeys::DeriveChildSkUnhardened(sk, index); } -G1Element CoreMPL::DeriveChildPkUnhardened(const G1Element& pk, uint32_t index) { +G1Element CoreMPL::DeriveChildPkUnhardened(const G1Element& pk, uint32_t index) +{ return HDKeys::DeriveChildG1Unhardened(pk, index); } -bool BasicSchemeMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) +bool BasicSchemeMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); const std::vector vecMessagesBytes(messages.begin(), messages.end()); - return BasicSchemeMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return BasicSchemeMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -405,8 +456,10 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); - if (arg_check != CONTINUE) return arg_check; + const auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); + if (arg_check != CONTINUE) + return arg_check; std::set> setMessages; for (const auto& message : messages) { @@ -428,30 +481,36 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeyElements, messages, signatureElement); } -bool BasicSchemeMPL::AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } - const std::set> setMessages(messages.begin(), messages.end()); + const std::set> setMessages( + messages.begin(), messages.end()); if (setMessages.size() != nPubKeys) { return false; } return CoreMPL::AggregateVerify(pubkeys, messages, signature); } -bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector &messages, - const G2Element& signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); - if (arg_check != CONTINUE) return arg_check; + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + if (arg_check != CONTINUE) + return arg_check; std::set> setMessages; for (const auto& message : messages) { @@ -463,7 +522,9 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeys, messages, signature); } -G2Element AugSchemeMPL::Sign(const PrivateKey &seckey, const vector &message) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const vector& message) { return AugSchemeMPL::Sign(seckey, message, seckey.GetG1Element()); } @@ -474,17 +535,19 @@ G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) } // Used for prepending different augMessage -G2Element AugSchemeMPL::Sign(const PrivateKey &seckey, - const vector &message, - const G1Element &prepend_pk) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const vector& message, + const G1Element& prepend_pk) { return AugSchemeMPL::Sign(seckey, Bytes(message), prepend_pk); } // Used for prepending different augMessage -G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, - const Bytes& message, - const G1Element& prepend_pk) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const Bytes& message, + const G1Element& prepend_pk) { vector augMessage = prepend_pk.Serialize(); augMessage.reserve(augMessage.size() + message.size()); @@ -492,9 +555,10 @@ G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, return CoreMPL::Sign(seckey, augMessage); } -bool AugSchemeMPL::Verify(const vector &pubkey, - const vector &message, - const vector &signature) +bool AugSchemeMPL::Verify( + const vector& pubkey, + const vector& message, + const vector& signature) { vector augMessage(pubkey); augMessage.reserve(augMessage.size() + message.size()); @@ -502,9 +566,10 @@ bool AugSchemeMPL::Verify(const vector &pubkey, return CoreMPL::Verify(pubkey, augMessage, signature); } -bool AugSchemeMPL::Verify(const Bytes& pubkey, - const Bytes& message, - const Bytes& signature) +bool AugSchemeMPL::Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) { vector augMessage(pubkey.begin(), pubkey.end()); augMessage.reserve(augMessage.size() + message.size()); @@ -512,16 +577,18 @@ bool AugSchemeMPL::Verify(const Bytes& pubkey, return CoreMPL::Verify(pubkey, Bytes(augMessage), Bytes(signature)); } -bool AugSchemeMPL::Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature) +bool AugSchemeMPL::Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature) { return AugSchemeMPL::Verify(pubkey, Bytes(message), signature); } -bool AugSchemeMPL::Verify(const G1Element& pubkey, - const Bytes& message, - const G2Element& signature) +bool AugSchemeMPL::Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) { vector augMessage = pubkey.Serialize(); augMessage.reserve(augMessage.size() + message.size()); @@ -529,18 +596,21 @@ bool AugSchemeMPL::Verify(const G1Element& pubkey, return CoreMPL::Verify(pubkey, augMessage, signature); } -bool AugSchemeMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) +bool AugSchemeMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) { std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); std::vector vecMessagesBytes(messages.begin(), messages.end()); - return AugSchemeMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return AugSchemeMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) { size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -549,7 +619,8 @@ bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); + auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); if (arg_check != CONTINUE) { return arg_check; } @@ -562,30 +633,34 @@ bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, aug.insert(aug.end(), messages[i].begin(), messages[i].end()); } - std::vector vecAugMessageBytes(augMessages.begin(), augMessages.end()); + std::vector vecAugMessageBytes( + augMessages.begin(), augMessages.end()); return CoreMPL::AggregateVerify(pubkeys, vecAugMessageBytes, signature); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector>& messages, - const G2Element& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { std::vector vecMessagesBytes(messages.begin(), messages.end()); return AugSchemeMPL::AggregateVerify(pubkeys, vecMessagesBytes, signature); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { size_t nPubKeys = pubkeys.size(); - auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } vector> augMessages(nPubKeys); - for (int i = 0; i < nPubKeys; ++i) { + for (size_t i = 0; i < nPubKeys; ++i) { vector& aug = augMessages[i]; vector&& pubkey = pubkeys[i].Serialize(); aug.reserve(pubkey.size() + messages[i].size()); @@ -612,7 +687,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BnArrayGuard computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(false); @@ -621,7 +696,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].data(); }); std::vector weightedPks(vecSorted.size()); @@ -637,37 +712,40 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return CoreMPL::AggregateVerify(weightedPks, augMessages, signature); } -G2Element PopSchemeMPL::PopProve(const PrivateKey &seckey) +G2Element PopSchemeMPL::PopProve(const PrivateKey& seckey) { - const G1Element& pk = seckey.GetG1Element(); - const G2Element hashedKey = G2Element::FromMessage(pk.SerializeToArray(), (const uint8_t *)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); - return seckey.GetG2Power(hashedKey); -} + std::array pubkey_bytes = seckey.GetG1Element().SerializeToArray(); + return seckey.SignG2( + pubkey_bytes.data(), + pubkey_bytes.size(), + (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), + POP_CIPHERSUITE_ID.length()); +} -bool PopSchemeMPL::PopVerify(const G1Element &pubkey, const G2Element &signature_proof) +bool PopSchemeMPL::PopVerify( + const G1Element& pubkey, + const G2Element& signature_proof) { + // Same semantics as CoreMPL::Verify (and the relic implementation): the + // elements are gated by IsValid() and the check is the product of + // pairings rather than blst_core_verify. if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature_proof.IsValid() || signature_proof == G2Element()) { return false; } - - const G2Element hashedPoint = G2Element::FromMessage(pubkey.SerializeToArray(), (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); - - g1_t g1s[2]; - g2_t g2s[2]; - - G1Element::Generator().Negate().ToNative(g1s[0]); - pubkey.ToNative(g1s[1]); - signature_proof.ToNative(g2s[0]); - hashedPoint.ToNative(g2s[1]); - - return CoreMPL::NativeVerify(g1s, g2s, 2); + const std::array pubkey_bytes = pubkey.SerializeToArray(); + const G2Element hashedPoint = G2Element::FromMessage(Bytes(pubkey_bytes), (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature_proof, hashedPoint}; + return CoreMPL::NativeVerify(g1s, g2s); } -bool PopSchemeMPL::PopVerify(const vector &pubkey, const vector &proof) +bool PopSchemeMPL::PopVerify( + const vector& pubkey, + const vector& proof) { return PopSchemeMPL::PopVerify(Bytes(pubkey), Bytes(proof)); } @@ -685,16 +763,19 @@ bool PopSchemeMPL::PopVerify(const Bytes& pubkey, const Bytes& proof) return PopSchemeMPL::PopVerify(pk, proofElement); } -bool PopSchemeMPL::FastAggregateVerify(const vector &pubkeys, - const vector &message, - const G2Element &signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const vector& message, + const G2Element& signature) { - return PopSchemeMPL::FastAggregateVerify(pubkeys, Bytes(message), signature); + return PopSchemeMPL::FastAggregateVerify( + pubkeys, Bytes(message), signature); } -bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const G2Element& signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const G2Element& signature) { if (pubkeys.size() == 0) { return false; @@ -707,21 +788,25 @@ bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, return false; } } - // No VerifyAggregateSignatureArguments checks required here as we have exactly one pubkey and one message. + // No VerifyAggregateSignatureArguments checks required here as we have + // exactly one pubkey and one message. return CoreMPL::Verify(CoreMPL::Aggregate(pubkeys), message, signature); } -bool PopSchemeMPL::FastAggregateVerify(const vector> &pubkeys, - const vector &message, - const vector &signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector>& pubkeys, + const vector& message, + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); - return PopSchemeMPL::FastAggregateVerify(vecPubKeyBytes, Bytes(message), Bytes(signature)); + return PopSchemeMPL::FastAggregateVerify( + vecPubKeyBytes, Bytes(message), Bytes(signature)); } -bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const Bytes& signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); if (nPubKeys == 0) { @@ -739,7 +824,8 @@ bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, return false; } - return PopSchemeMPL::FastAggregateVerify(pkelements, message, signatureElement); + return PopSchemeMPL::FastAggregateVerify( + pkelements, message, signatureElement); } G2Element LegacySchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) @@ -749,21 +835,19 @@ G2Element LegacySchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) bool LegacySchemeMPL::Verify(const G1Element &pubkey, const Bytes& message, const G2Element &signature) { - g1_t g1s[2]; - g2_t g2s[2]; - - G1Element::Generator().Negate().ToNative(g1s[0]); + // the legacy scheme is defined on 32-byte message hashes + if (message.size() != BLS::MESSAGE_HASH_LEN) { + return false; + } if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature.IsValid() || signature == G2Element()) { return false; } - pubkey.ToNative(g1s[1]); - signature.ToNative(g2s[0]); - G2Element::FromMessage(message, nullptr, 0, true).ToNative(g2s[1]); - - return CoreMPL::NativeVerify(g1s, g2s, 2); + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature, G2Element::FromMessage(message, nullptr, 0, true)}; + return CoreMPL::NativeVerify(g1s, g2s); } G2Element LegacySchemeMPL::AggregateSecure(std::vector const &vecPublicKeys, @@ -786,23 +870,27 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) return arg_check; - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToNative(&vecG1[0]); if (!signature.IsValid() || signature == G2Element()) { return false; } - signature.ToNative(&vecG2[0]); - + std::vector vecG1; + std::vector vecG2; + vecG1.reserve(nPubKeys + 1); + vecG2.reserve(nPubKeys + 1); + vecG1.push_back(G1Element::Generator().Negate()); + vecG2.push_back(signature); for (size_t i = 0; i < nPubKeys; ++i) { + // the legacy scheme is defined on 32-byte message hashes + if (messages[i].size() != BLS::MESSAGE_HASH_LEN) { + return false; + } if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { return false; } - pubkeys[i].ToNative(&vecG1[i + 1]); - G2Element::FromMessage(messages[i], nullptr, 0, true).ToNative(&vecG2[i + 1]); + vecG1.push_back(pubkeys[i]); + vecG2.push_back(G2Element::FromMessage(messages[i], nullptr, 0, true)); } - - return CoreMPL::NativeVerify((g1_t*)vecG1.data(), (g2_t*)vecG2.data(), nPubKeys + 1); + return CoreMPL::NativeVerify(vecG1, vecG2); } } // end namespace bls diff --git a/src/test-bench.cpp b/src/test-bench.cpp index 20bbbb443..0cc476790 100644 --- a/src/test-bench.cpp +++ b/src/test-bench.cpp @@ -13,22 +13,27 @@ // limitations under the License. #include + #include "bls.hpp" #include "test-utils.hpp" -extern "C" { -#include "relic.h" -} - using std::string; using std::vector; -using std::cout; -using std::endl; using namespace bls; +std::vector wjbgetRandomSeed() +{ + uint8_t buf[32]; + + for (int i = 0; i < 32; i++) buf[i] = rand(); -void benchSigs() { + std::vector ret(buf, buf + 32); + return ret; +} + +void benchSigs() +{ string testName = "Signing"; const int numIters = 5000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); @@ -42,21 +47,22 @@ void benchSigs() { endStopwatch(testName, start, numIters); } -void benchVerification() { +void benchVerification() +{ string testName = "Verification"; const int numIters = 1000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); - std::vector sigs; + vector pkBytes = pk.Serialize(); for (int i = 0; i < numIters; i++) { uint8_t message[4]; Util::IntToFourBytes(message, i); vector messageBytes(message, message + 4); - sigs.push_back(BasicSchemeMPL().Sign(sk, messageBytes)); + G2Element sig = BasicSchemeMPL().Sign(sk, messageBytes); + sigs.push_back(sig); } - auto start = startStopwatch(); for (int i = 0; i < numIters; i++) { uint8_t message[4]; @@ -68,7 +74,8 @@ void benchVerification() { endStopwatch(testName, start, numIters); } -void benchBatchVerification() { +void benchBatchVerification() +{ const int numIters = 10000; vector> sig_bytes; @@ -116,7 +123,8 @@ void benchBatchVerification() { endStopwatch("Batch verification", start, numIters); } -void benchSerialize() { +void benchSerialize() +{ const int numIters = 5000000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); @@ -142,7 +150,8 @@ void benchSerialize() { endStopwatch("Serialize G2Element", start, numIters); } -void benchSerializeToArray() { +void benchSerializeToArray() +{ const int numIters = 5000000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); @@ -168,7 +177,8 @@ void benchSerializeToArray() { endStopwatch("SerializeToArray G2Element", start, numIters); } -int main(int argc, char* argv[]) { +int main() +{ benchSigs(); benchVerification(); benchBatchVerification(); diff --git a/src/test-vectors.hpp b/src/test-vectors.hpp new file mode 100644 index 000000000..430d7ec87 --- /dev/null +++ b/src/test-vectors.hpp @@ -0,0 +1,575 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SRC_TEST_VECTORS_HPP_ +#define SRC_TEST_VECTORS_HPP_ + +// Reference consensus vectors. +// +// GenerateReferenceVectors() deterministically exercises the public dashbls +// API - legacy and basic serialization (including malformed input), legacy +// signing, secure aggregation, threshold signing, extended keys and the +// private key edge cases - and emits one "name=value" line per result. +// +// test-vectors/reference.txt holds the output of the relic-based library; +// runtest compares the current library against it line by line, and +// tools/vectorgen.cpp regenerates it. Only the public API is used so the +// same code builds against any backend. + +#include +#include +#include +#include +#include +#include + +#include "bls.hpp" + +namespace bls_test_vectors { + +using namespace bls; +using std::vector; + +struct Emitter { + std::vector lines; + + static std::string hex(const uint8_t* p, size_t n) + { + static const char* digits = "0123456789abcdef"; + std::string s; + s.reserve(n * 2); + for (size_t i = 0; i < n; i++) { + s.push_back(digits[p[i] >> 4]); + s.push_back(digits[p[i] & 0x0f]); + } + return s; + } + + void raw(const std::string& line) { lines.push_back(line); } + void dump(const std::string& name, const vector& v) { lines.push_back(name + "=" + hex(v.data(), v.size())); } + template + void dump(const std::string& name, const std::array& v) { lines.push_back(name + "=" + hex(v.data(), N)); } + void dumpb(const std::string& name, bool v) { lines.push_back(name + "=" + (v ? "1" : "0")); } + void dumpu(const std::string& name, uint32_t v) + { + char buf[16]; + snprintf(buf, sizeof(buf), "%08x", v); + lines.push_back(name + "=" + buf); + } + void exc(const std::string& name) { lines.push_back(name + "=EXC"); } +}; + +inline vector seedN(int i) +{ + vector s(32); + for (int j = 0; j < 32; j++) s[j] = (uint8_t)(0x11 * (i + 1) + j * 7); + return s; +} + +inline vector hashN(int i) +{ + vector h(32); + for (int j = 0; j < 32; j++) h[j] = (uint8_t)(0xA5 ^ (i * 31 + j * 13)); + return h; +} + +inline std::string tag(const char* base, int i) +{ + char buf[96]; + snprintf(buf, sizeof(buf), "%s.%d", base, i); + return std::string(buf); +} + +// ---------------------------------------------------------------- section A/B +inline void KeysAndSignatures(Emitter& e) +{ + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (int i = 0; i < 8; i++) { + PrivateKey sk = basic.KeyGen(seedN(i)); + e.dump(tag("A.sk", i), sk.Serialize()); + + G1Element pk = sk.GetG1Element(); + e.dump(tag("A.pk.basic", i), pk.Serialize(false)); + e.dump(tag("A.pk.legacy", i), pk.Serialize(true)); + e.dumpu(tag("A.fp.basic", i), pk.GetFingerprint(false)); + e.dumpu(tag("A.fp.legacy", i), pk.GetFingerprint(true)); + e.dump(tag("A.skG2.basic", i), sk.GetG2Element().Serialize(false)); + e.dump(tag("A.skG2.legacy", i), sk.GetG2Element().Serialize(true)); + + // round-trips through both codecs + e.dump(tag("A.pk.rt.legacy", i), + G1Element::FromBytes(Bytes(pk.Serialize(true)), true).Serialize(true)); + e.dump(tag("A.pk.rt.basic", i), + G1Element::FromBytes(Bytes(pk.Serialize(false)), false).Serialize(false)); + + // legacy sign over a 32-byte hash; serialize under both codecs + vector h = hashN(i); + G2Element lsig = legacy.Sign(sk, Bytes(h)); + e.dump(tag("B.lsig.legacy", i), lsig.Serialize(true)); + e.dump(tag("B.lsig.basic", i), lsig.Serialize(false)); + e.dumpb(tag("B.lverify", i), legacy.Verify(pk, Bytes(h), lsig)); + // wrong message must fail + vector h2 = hashN(i + 100); + e.dumpb(tag("B.lverify.bad", i), legacy.Verify(pk, Bytes(h2), lsig)); + // legacy sig round-trip through legacy codec + e.dump(tag("B.lsig.rt", i), + G2Element::FromBytes(Bytes(lsig.Serialize(true)), true).Serialize(true)); + + // basic scheme (arbitrary-length message) + vector msg = {(uint8_t)i, 1, 2, 3, 200, 255}; + G2Element bsig = basic.Sign(sk, msg); + e.dump(tag("B.bsig.basic", i), bsig.Serialize(false)); + e.dump(tag("B.bsig.legacy", i), bsig.Serialize(true)); + e.dumpb(tag("B.bverify", i), basic.Verify(pk, msg, bsig)); + + // EIP-2333 derivation + e.dump(tag("A.child.hard", i), basic.DeriveChildSk(sk, 0x8000002a).Serialize()); + e.dump(tag("A.child.soft", i), basic.DeriveChildSkUnhardened(sk, 1337).Serialize()); + } +} + +// ---------------------------------------------------------------- section C/D +inline void SecureAggregation(Emitter& e) +{ + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (int n : {1, 2, 3, 10}) { + vector sks; + vector pks; + vector lsigs, bsigs; + vector h = hashN(n); + for (int i = 0; i < n; i++) { + sks.push_back(basic.KeyGen(seedN(200 + n * 16 + i))); + pks.push_back(sks.back().GetG1Element()); + lsigs.push_back(legacy.Sign(sks.back(), Bytes(h))); + bsigs.push_back(basic.Sign(sks.back(), Bytes(h))); + } + G2Element lagg = legacy.AggregateSecure(pks, lsigs, Bytes(h)); + e.dump(tag("C.aggsec.legacy", n), lagg.Serialize(true)); + e.dumpb(tag("C.versec.legacy", n), legacy.VerifySecure(pks, lagg, Bytes(h))); + + G2Element bagg = basic.AggregateSecure(pks, bsigs, Bytes(h)); + e.dump(tag("C.aggsec.basic", n), bagg.Serialize(false)); + e.dumpb(tag("C.versec.basic", n), basic.VerifySecure(pks, bagg, Bytes(h))); + + // cross checks must fail + e.dumpb(tag("C.versec.legacy.bad", n), + legacy.VerifySecure(pks, lagg, Bytes(hashN(n + 50)))); + + // plain aggregation + e.dump(tag("C.agg.G2", n), basic.Aggregate(bsigs).Serialize(false)); + e.dump(tag("C.agg.G1", n), basic.Aggregate(pks).Serialize(false)); + e.dump(tag("C.agg.sk", n), PrivateKey::Aggregate(sks).Serialize()); + } + + // legacy multi-message AggregateVerify + { + int n = 4; + vector sks; + vector pks; + vector sigs; + vector> msgs; + for (int i = 0; i < n; i++) { + sks.push_back(basic.KeyGen(seedN(300 + i))); + pks.push_back(sks.back().GetG1Element()); + msgs.push_back(hashN(300 + i)); + sigs.push_back(legacy.Sign(sks.back(), Bytes(msgs.back()))); + } + G2Element agg = basic.Aggregate(sigs); // plain point sum + vector msgRefs; + for (auto& m : msgs) msgRefs.push_back(Bytes(m)); + e.dump("D.lagg", agg.Serialize(true)); + e.dumpb("D.laggverify", legacy.AggregateVerify(pks, msgRefs, agg)); + vector badRefs; + badRefs.push_back(Bytes(msgs[1])); + for (size_t i = 1; i < msgs.size(); i++) badRefs.push_back(Bytes(msgs[i])); + e.dumpb("D.laggverify.bad", legacy.AggregateVerify(pks, badRefs, agg)); + } +} + +// ------------------------------------------------------------------ section E +inline void ThresholdVectors(Emitter& e) +{ + BasicSchemeMPL basic; + const int m = 3, n = 5; + + // polynomial coefficients (the "master" keys) + vector coeffs; + vector vvec; + vector sigCoeffs; + vector h = hashN(77); + for (int i = 0; i < m; i++) { + coeffs.push_back(basic.KeyGen(seedN(400 + i))); + vvec.push_back(coeffs.back().GetG1Element()); + sigCoeffs.push_back(Threshold::Sign(coeffs[i], Bytes(h))); + } + e.dump("E.mastersig", Threshold::Sign(coeffs[0], Bytes(h)).Serialize(true)); + e.dumpb("E.masterverify", + Threshold::Verify(vvec[0], Bytes(h), Threshold::Sign(coeffs[0], Bytes(h)))); + + vector> ids; + for (int i = 0; i < n; i++) ids.push_back(hashN(500 + i)); + + vector skShares; + vector sigShares; + for (int i = 0; i < n; i++) { + PrivateKey sh = Threshold::PrivateKeyShare(coeffs, Bytes(ids[i])); + skShares.push_back(sh); + e.dump(tag("E.skshare", i), sh.Serialize()); + G1Element pkShare = Threshold::PublicKeyShare(vvec, Bytes(ids[i])); + e.dump(tag("E.pkshare", i), pkShare.Serialize(true)); + e.dumpb(tag("E.sharematch", i), pkShare == sh.GetG1Element()); + G2Element sigShare = Threshold::SignatureShare(sigCoeffs, Bytes(ids[i])); + e.dump(tag("E.sigshare.poly", i), sigShare.Serialize(true)); + G2Element sigShare2 = Threshold::Sign(sh, Bytes(h)); + e.dump(tag("E.sigshare.sign", i), sigShare2.Serialize(true)); + sigShares.push_back(sigShare2); + } + + // recover from first m shares + vector mSk(skShares.begin(), skShares.begin() + m); + vector mSig(sigShares.begin(), sigShares.begin() + m); + vector mIds; + for (int i = 0; i < m; i++) mIds.push_back(Bytes(ids[i])); + + PrivateKey skRec = Threshold::PrivateKeyRecover(mSk, mIds); + e.dump("E.skrecover", skRec.Serialize()); + e.dumpb("E.skrecover.match", skRec == coeffs[0]); + + vector mPk; + for (int i = 0; i < m; i++) mPk.push_back(skShares[i].GetG1Element()); + e.dump("E.pkrecover", Threshold::PublicKeyRecover(mPk, mIds).Serialize(true)); + + G2Element sigRec = Threshold::SignatureRecover(mSig, mIds); + e.dump("E.sigrecover", sigRec.Serialize(true)); + e.dumpb("E.sigrecover.verify", Threshold::Verify(vvec[0], Bytes(h), sigRec)); + + // recovery from a different share subset must give the same signature + vector mSig2(sigShares.begin() + 1, sigShares.begin() + 1 + m); + vector mIds2; + for (int i = 1; i < 1 + m; i++) mIds2.push_back(Bytes(ids[i])); + e.dumpb("E.sigrecover.same", + Threshold::SignatureRecover(mSig2, mIds2) == sigRec); +} + +// ------------------------------------------------------------------ section F +inline void ExtendedKeys(Emitter& e) +{ + vector seed = seedN(600); + ExtendedPrivateKey esk = ExtendedPrivateKey::FromSeed(Bytes(seed)); + e.dump("F.esk", esk.Serialize()); + e.dump("F.esk.pk.legacy", esk.GetPublicKey().Serialize(true)); + e.dump("F.esk.pk.basic", esk.GetPublicKey().Serialize(false)); + e.dump("F.esk.chaincode", esk.GetChainCode().Serialize()); + + // legacy derivation chain (fLegacy defaults true on these APIs) + ExtendedPrivateKey c1l = esk.PrivateChild(1, true); + ExtendedPrivateKey c2l = c1l.PrivateChild(0x8000002a, true); + e.dump("F.child.legacy", c2l.Serialize()); + e.dump("F.child.legacy.sk", c2l.GetPrivateKey().Serialize()); + + // modern derivation chain + ExtendedPrivateKey c1b = esk.PrivateChild(1, false); + ExtendedPrivateKey c2b = c1b.PrivateChild(0x8000002a, false); + e.dump("F.child.basic", c2b.Serialize()); + e.dump("F.child.basic.sk", c2b.GetPrivateKey().Serialize()); + + // extended public keys and public derivation + ExtendedPublicKey epkl = esk.GetExtendedPublicKey(true); + e.dump("F.epk.legacy", epkl.Serialize(true)); + ExtendedPublicKey epkb = esk.GetExtendedPublicKey(false); + e.dump("F.epk.basic", epkb.Serialize(false)); + e.dump("F.epk.child.legacy", epkl.PublicChild(1, true).Serialize(true)); + e.dump("F.epk.child.basic", epkb.PublicChild(1, false).Serialize(false)); + e.dumpb("F.pubpriv.match.legacy", + epkl.PublicChild(1, true).GetPublicKey() == + esk.PrivateChild(1, true).GetPublicKey()); + + // round-trips + e.dump("F.esk.rt", + ExtendedPrivateKey::FromBytes(Bytes(esk.Serialize())).Serialize()); + e.dump("F.epk.rt.legacy", + ExtendedPublicKey::FromBytes(Bytes(epkl.Serialize(true)), true).Serialize(true)); +} + +// ------------------------------------------------------------------ section G +inline void PrivateKeyEdges(Emitter& e) +{ + // in-range key round-trip, both modOrder values + PrivateKey sk = BasicSchemeMPL().KeyGen(seedN(700)); + vector raw = sk.Serialize(); + e.dump("G.inrange.mod0", PrivateKey::FromBytes(Bytes(raw), false).Serialize()); + e.dump("G.inrange.mod1", PrivateKey::FromBytes(Bytes(raw), true).Serialize()); + + // value >= group order: modOrder=true reduces, modOrder=false throws + vector big(32, 0xff); + try { e.dump("G.big.mod1", PrivateKey::FromBytes(Bytes(big), true).Serialize()); } + catch (const std::exception&) { e.exc("G.big.mod1"); } + try { e.dump("G.big.mod0", PrivateKey::FromBytes(Bytes(big), false).Serialize()); } + catch (const std::exception&) { e.exc("G.big.mod0"); } + + // the group order itself, and order-1 + // r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + vector order = { + 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, + 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, + 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}; + try { e.dump("G.order.mod1", PrivateKey::FromBytes(Bytes(order), true).Serialize()); } + catch (const std::exception&) { e.exc("G.order.mod1"); } + try { e.dump("G.order.mod0", PrivateKey::FromBytes(Bytes(order), false).Serialize()); } + catch (const std::exception&) { e.exc("G.order.mod0"); } + vector orderm1 = order; + orderm1[31] = 0x00; + try { e.dump("G.orderm1.mod0", PrivateKey::FromBytes(Bytes(orderm1), false).Serialize()); } + catch (const std::exception&) { e.exc("G.orderm1.mod0"); } + + // zero key + vector zero(32, 0x00); + try { + PrivateKey z = PrivateKey::FromBytes(Bytes(zero), false); + e.dump("G.zero.sk", z.Serialize()); + e.dump("G.zero.pk", z.GetG1Element().Serialize(false)); + } catch (const std::exception&) { e.exc("G.zero.sk"); } + + // FromSeedBIP32 + e.dump("G.bip32seed", PrivateKey::FromSeedBIP32(Bytes(seedN(701))).Serialize()); + + // DH key exchange: sk_a * pk_b == sk_b * pk_a + PrivateKey a = BasicSchemeMPL().KeyGen(seedN(702)); + PrivateKey b = BasicSchemeMPL().KeyGen(seedN(703)); + G1Element dh = a * b.GetG1Element(); + e.dump("G.dh", dh.Serialize(false)); + e.dumpb("G.dh.sym", dh == (b * a.GetG1Element())); +} + +// ------------------------------------------------------------------ section H +// Crafted byte strings through every decode path. For each: either the +// round-trip hex (proving accept + canonical re-encoding) or EXC. +inline void decodeProbe(Emitter& e, const std::string& name, const vector& in, bool fLegacy) +{ + bool isG1 = in.size() == 48; + try { + if (isG1) { + G1Element el = G1Element::FromBytes(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } else { + G2Element el = G2Element::FromBytes(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } + } catch (const std::exception&) { e.exc(name); } +} + +inline void decodeProbeUnchecked(Emitter& e, const std::string& name, const vector& in, bool fLegacy) +{ + bool isG1 = in.size() == 48; + try { + if (isG1) { + G1Element el = G1Element::FromBytesUnchecked(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } else { + G2Element el = G2Element::FromBytesUnchecked(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } + } catch (const std::exception&) { e.exc(name); } +} + +inline vector fromHex(const char* hex) +{ + vector out; + for (size_t i = 0; hex[i] && hex[i + 1]; i += 2) { + auto nyb = [](char c) -> int { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return c - 'A' + 10; + }; + out.push_back((uint8_t)((nyb(hex[i]) << 4) | nyb(hex[i + 1]))); + } + return out; +} + +inline void EdgeDecodes(Emitter& e) +{ + BasicSchemeMPL basic; + PrivateKey sk = basic.KeyGen(seedN(800)); + G1Element pk = sk.GetG1Element(); + G2Element sig = LegacySchemeMPL().Sign(sk, Bytes(hashN(800))); + + // 1. canonical infinity, both sizes, both modes + vector inf48(48, 0); + inf48[0] = 0xc0; + vector inf96(96, 0); + inf96[0] = 0xc0; + decodeProbe(e, "H.g1.inf.basic", inf48, false); + decodeProbe(e, "H.g1.inf.legacy", inf48, true); + decodeProbe(e, "H.g2.inf.basic", inf96, false); + decodeProbe(e, "H.g2.inf.legacy", inf96, true); + + // 2. non-canonical infinity (0xc0 with a trailing nonzero byte) + vector badInf48 = inf48; + badInf48[47] = 0x01; + vector badInf96 = inf96; + badInf96[95] = 0x01; + decodeProbe(e, "H.g1.badinf.basic", badInf48, false); + decodeProbe(e, "H.g1.badinf.legacy", badInf48, true); + decodeProbe(e, "H.g2.badinf.basic", badInf96, false); + decodeProbe(e, "H.g2.badinf.legacy", badInf96, true); + + // 3. all zeros + decodeProbe(e, "H.g1.zeros.basic", vector(48, 0), false); + decodeProbe(e, "H.g1.zeros.legacy", vector(48, 0), true); + decodeProbe(e, "H.g2.zeros.basic", vector(96, 0), false); + decodeProbe(e, "H.g2.zeros.legacy", vector(96, 0), true); + decodeProbeUnchecked(e, "H.g1.zeros.legacy.unchk", vector(48, 0), true); + decodeProbeUnchecked(e, "H.g2.zeros.legacy.unchk", vector(96, 0), true); + + // 4. x >= field prime (0xff filler), with/without legacy sign bit + vector ff48(48, 0xff); + vector ff96(96, 0xff); + decodeProbe(e, "H.g1.ff.basic", ff48, false); + decodeProbe(e, "H.g1.ff.legacy", ff48, true); + decodeProbe(e, "H.g2.ff.basic", ff96, false); + decodeProbe(e, "H.g2.ff.legacy", ff96, true); + vector ff48low = ff48; + ff48low[0] = 0x1f; // below prime top byte range but x still >= p + decodeProbe(e, "H.g1.fflow.legacy", ff48low, true); + + // 5. legacy bytes fed to the modern decoder and vice versa + decodeProbe(e, "H.g1.legacyAsBasic", pk.Serialize(true), false); + decodeProbe(e, "H.g1.basicAsLegacy", pk.Serialize(false), true); + decodeProbe(e, "H.g2.legacyAsBasic", sig.Serialize(true), false); + decodeProbe(e, "H.g2.basicAsLegacy", sig.Serialize(false), true); + + // 6. valid encodings with flipped sign bit (the negated point) + vector pkl = pk.Serialize(true); + pkl[0] ^= 0x80; + decodeProbe(e, "H.g1.negated.legacy", pkl, true); + vector pkb = pk.Serialize(false); + pkb[0] ^= 0x20; + decodeProbe(e, "H.g1.negated.basic", pkb, false); + + // 7. on-curve but non-subgroup points (found by scanning small x with a + // blst helper; each x satisfies y^2 = x^3 + 4 (+4i) with the point + // outside the r-torsion). Encodings below use y = the "smaller" root + // (sign bit 0), in each codec's byte layout. + // G1: x = 4 -> not in subgroup. + vector nsG1(48, 0); + nsG1[47] = 0x04; + decodeProbe(e, "H.g1.nonsub.legacy", nsG1, true); + decodeProbeUnchecked(e, "H.g1.nonsub.legacy.unchk", nsG1, true); + vector nsG1b = nsG1; + nsG1b[0] |= 0x80; // modern compression flag + decodeProbe(e, "H.g1.nonsub.basic", nsG1b, false); + decodeProbeUnchecked(e, "H.g1.nonsub.basic.unchk", nsG1b, false); + // G2: x = (1, 0) -> not in subgroup (legacy layout: c0 || c1). + vector nsG2(96, 0); + nsG2[47] = 0x01; + decodeProbe(e, "H.g2.nonsub.legacy", nsG2, true); + decodeProbeUnchecked(e, "H.g2.nonsub.legacy.unchk", nsG2, true); + // modern layout: c1 || c0 with flags on byte 0 + vector nsG2b(96, 0); + nsG2b[95] = 0x01; + nsG2b[0] |= 0x80; + decodeProbe(e, "H.g2.nonsub.basic", nsG2b, false); + decodeProbeUnchecked(e, "H.g2.nonsub.basic.unchk", nsG2b, false); + + // 8. x not on curve (no square root exists): x = 1 for G1 + vector noc(48, 0); + noc[47] = 0x01; + decodeProbe(e, "H.g1.nocurve.legacy", noc, true); + vector nocb = noc; + nocb[0] |= 0x80; + decodeProbe(e, "H.g1.nocurve.basic", nocb, false); + + // 9. modern G2 with the reserved top bits of byte 48 set + vector flag48 = sig.Serialize(false); + flag48[48] |= 0xe0; + decodeProbe(e, "H.g2.flag48.basic", flag48, false); + + // 10. wrong length + try { + G1Element::FromBytes(Bytes(vector(47, 0)), false); + e.raw("H.len47=NOEXC"); + } catch (const std::exception&) { e.exc("H.len47"); } + + // 11. legacy sign bit set on degenerate x values (exercises the + // sqrt-failure and x>=p fallback paths with stored sign = 1) + vector s1(48, 0); + s1[0] = 0x80; + s1[47] = 0x01; // x = 1, not on curve + decodeProbe(e, "H.g1.nocurve.sign.legacy", s1, true); + vector sp(48, 0xff); + sp[0] = 0x9f; // sign bit + x = 0x1fff... >= p + decodeProbe(e, "H.g1.overp.sign.legacy", sp, true); + vector g2s(96, 0); + g2s[0] = 0x80; + g2s[47] = 0x02; // x = (2, 0), sign bit set + decodeProbe(e, "H.g2.x2.sign.legacy", g2s, true); + vector g2s3(96, 0); + g2s3[0] = 0x80; + g2s3[47] = 0x03; // x = (3, 0), sign bit set + decodeProbe(e, "H.g2.x3.sign.legacy", g2s3, true); + vector g2nc(96, 0); + g2nc[47] = 0x02; // x = (2, 0), sign bit clear + decodeProbe(e, "H.g2.x2.legacy", g2nc, true); + vector g2sp(96, 0xff); + g2sp[0] = 0x9f; // c0 >= p (sign set), c1 >= p + decodeProbe(e, "H.g2.overp.sign.legacy", g2sp, true); +} + +// ------------------------------------------------------------------ section I +// Verification semantics with infinity elements and empty inputs. +inline void VerifyEdges(Emitter& e) +{ + BasicSchemeMPL basic; + LegacySchemeMPL legacy; + vector msg = {9, 9, 9}; + vector h = hashN(900); + + e.dumpb("I.basic.verify.infinf", basic.Verify(G1Element(), msg, G2Element())); + e.dumpb("I.legacy.verify.infinf", legacy.Verify(G1Element(), Bytes(h), G2Element())); + + PrivateKey sk = basic.KeyGen(seedN(900)); + G2Element sig = basic.Sign(sk, msg); + e.dumpb("I.basic.verify.infpk", basic.Verify(G1Element(), msg, sig)); + e.dumpb("I.basic.verify.infsig", basic.Verify(sk.GetG1Element(), msg, G2Element())); + + // AggregateVerify argument invariants + e.dumpb("I.aggver.empty.infsig", + basic.AggregateVerify(vector{}, vector{}, G2Element())); + e.dumpb("I.aggver.empty.realsig", + basic.AggregateVerify(vector{}, vector{}, sig)); + e.dumpb("I.aggver.mismatch", + basic.AggregateVerify(vector{sk.GetG1Element()}, vector{}, sig)); + + // VerifySecure with an infinity pubkey in the list + vector pks = {sk.GetG1Element(), G1Element()}; + e.dumpb("I.versec.withinf", basic.VerifySecure(pks, sig, Bytes(h))); +} + + +inline std::vector GenerateReferenceVectors() +{ + Emitter e; + e.raw("# dashbls reference vectors v1"); + KeysAndSignatures(e); + SecureAggregation(e); + ThresholdVectors(e); + ExtendedKeys(e); + PrivateKeyEdges(e); + EdgeDecodes(e); + VerifyEdges(e); + e.raw("# end"); + return e.lines; +} + +} // namespace bls_test_vectors + +#endif // SRC_TEST_VECTORS_HPP_ diff --git a/src/test.cpp b/src/test.cpp index c6fd55393..d6572cbe1 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -19,18 +19,25 @@ #include "bls.hpp" #include -extern "C" { -#include "relic.h" -} #include "test-utils.hpp" -using std::cout; -using std::endl; +#include "test-vectors.hpp" + +#include +#include + using std::string; using std::vector; using namespace bls; -void TestHKDF(string ikm_hex, string salt_hex, string info_hex, string prk_expected_hex, string okm_expected_hex, int L) { +void TestHKDF( + string ikm_hex, + string salt_hex, + string info_hex, + string prk_expected_hex, + string okm_expected_hex, + int L) +{ vector ikm = Util::HexToBytes(ikm_hex); vector salt = Util::HexToBytes(salt_hex); vector info = Util::HexToBytes(info_hex); @@ -42,42 +49,45 @@ void TestHKDF(string ikm_hex, string salt_hex, string info_hex, string prk_expec HKDF256::Expand(okm, L, prk, info.data(), info.size()); REQUIRE(32 == prk_expected.size()); - REQUIRE(L == okm_expected.size()); + REQUIRE(L == (int)okm_expected.size()); - for (size_t i=0; i < 32; i++) { + for (size_t i = 0; i < 32; i++) { REQUIRE(prk[i] == prk_expected[i]); } - for (size_t i=0; i < L; i++) { + for (int i = 0; i < L; i++) { REQUIRE(okm[i] == okm_expected[i]); } delete[] okm; } -TEST_CASE("class PrivateKey") { +TEST_CASE("class PrivateKey") +{ uint8_t buffer[PrivateKey::PRIVATE_KEY_SIZE]; memcpy(buffer, getRandomSeed().data(), PrivateKey::PRIVATE_KEY_SIZE); - SECTION("Copy {constructor|assignment operator}") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + SECTION("Copy {constructor|assignment operator}") + { + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = PrivateKey(pk2); REQUIRE(!pk1.IsZero()); REQUIRE(!pk2.IsZero()); REQUIRE(!pk3.IsZero()); REQUIRE(pk1 != pk2); REQUIRE(pk3 == pk2); - REQUIRE(pk2.GetG1Element().IsValid()); // cache previous g1 - REQUIRE(pk2.GetG2Element().IsValid()); // cache previous g2 + REQUIRE(pk2.GetG1Element().IsValid()); // cache previous g1 + REQUIRE(pk2.GetG2Element().IsValid()); // cache previous g2 pk2 = pk1; REQUIRE(pk1 == pk2); REQUIRE(pk1.GetG1Element() == pk2.GetG1Element()); REQUIRE(pk1.GetG2Element() == pk2.GetG2Element()); REQUIRE(pk3 != pk2); } - SECTION("Move {constructor|assignment operator}") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + SECTION("Move {constructor|assignment operator}") + { + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); std::vector vec1 = pk1.Serialize(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); std::vector vec2 = pk2.Serialize(); PrivateKey pk3 = PrivateKey(std::move(pk2)); REQUIRE(!pk1.IsZero()); @@ -94,48 +104,71 @@ TEST_CASE("class PrivateKey") { REQUIRE_THROWS(pk2.IsZero()); REQUIRE_THROWS(pk3.IsZero()); } - SECTION("Equality operators") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + SECTION("Equality operators") + { + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = pk2; REQUIRE(pk1 != pk2); REQUIRE(pk1 != pk3); REQUIRE(pk2 == pk3); } - SECTION("(De)Serialization") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + SECTION("(De)Serialization") + { + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); pk1.Serialize(buffer); - REQUIRE(memcmp(buffer, pk1.Serialize().data(), PrivateKey::PRIVATE_KEY_SIZE) == 0); + REQUIRE( + memcmp( + buffer, pk1.Serialize().data(), PrivateKey::PRIVATE_KEY_SIZE) == + 0); PrivateKey pk2 = PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true); REQUIRE(pk1 == pk2); - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE - 1), true)); - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE + 1), true)); - REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - bn_t order; - bn_new(order); - g1_get_ord(order); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); + REQUIRE_THROWS(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE - 1), true)); + REQUIRE_THROWS(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE + 1), true)); + REQUIRE_NOTHROW(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // the group order r itself is accepted (bn_cmp > 0 semantics) + const vector order = Util::HexToBytes( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + memcpy(buffer, order.data(), PrivateKey::PRIVATE_KEY_SIZE); REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - bn_add(order, order, order); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); + // 2 * r is above the order and must be rejected without modOrder + const vector order2 = Util::HexToBytes( + "e7db4ea6533afa906673b0101343b00aa77b4805fffcb7fdfffffffe00000002"); + memcpy(buffer, order2.data(), PrivateKey::PRIVATE_KEY_SIZE); REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // The hardcoded order must agree with blst's own, which is only + // reachable through blst_scalar_fr_check (true iff below r): r itself + // is not a valid Fr element, r - 1 is. + blst_scalar s; + blst_scalar_from_bendian(&s, order.data()); + REQUIRE(!blst_scalar_fr_check(&s)); + vector orderm1 = order; + orderm1[31] = 0x00; + blst_scalar_from_bendian(&s, orderm1.data()); + REQUIRE(blst_scalar_fr_check(&s)); } SECTION("BIP32 Seed") { uint8_t aliceSeed[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; PrivateKey pk1 = PrivateKey::FromSeedBIP32(Bytes(aliceSeed, 10)); REQUIRE(pk1.HasKeyData()); - vector privateKey = pk1.Serialize(true); + vector privateKey = pk1.Serialize(); vector knownPrivateKey = Util::HexToBytes("46891c2cec49593c81921e473db7480029e0fc1eb933c6b93d81f5370eb19fbd"); REQUIRE(privateKey == knownPrivateKey); G1Element pubKey1 = pk1.GetG1Element(); vector pubKey1Bytes = pubKey1.Serialize(true); + vector pubKey2Bytes = pubKey1.Serialize(false); vector knownPublicKey = Util::HexToBytes("1790635de8740e9a6a6b15fb6b72f3a16afa0973d971979b6ba54761d6e2502c50db76f4d26143f05459a42cfd520d44"); REQUIRE(pubKey1Bytes == knownPublicKey); + REQUIRE(pubKey2Bytes != pubKey1Bytes); } - SECTION("keydata checks") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + SECTION("keydata checks") + { + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); G1Element g1 = pk1.GetG1Element(); G2Element g2 = pk1.GetG2Element(); PrivateKey pk2 = std::move(pk1); @@ -154,69 +187,86 @@ TEST_CASE("class PrivateKey") { REQUIRE_THROWS(pk1 != pk2); REQUIRE_THROWS(pk1.Serialize(buffer)); REQUIRE_THROWS(pk1.Serialize()); - REQUIRE_THROWS(pk1.SignG2(buffer, sizeof(buffer), buffer, sizeof(buffer))); + REQUIRE_THROWS( + pk1.SignG2(buffer, sizeof(buffer), buffer, sizeof(buffer))); } } -TEST_CASE("HKDF") { +TEST_CASE("HKDF") +{ // https://tools.ietf.org/html/rfc5869 test vectors - SECTION("Test case 2") { - TestHKDF("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "000102030405060708090a0b0c", - "f0f1f2f3f4f5f6f7f8f9", - "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", - 42 - ); - } - SECTION("Test case 2") { - TestHKDF("000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f", // 80 octets - "0x606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", // 80 octets - "0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", // 80 octets - "0x06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", // 32 octets - "0xb11e398dc80327a1c8e7f78c596a4934" - "4f012eda2d4efad8a050cc4c19afa97c" - "59045a99cac7827271cb41c65e590e09" - "da3275600c2f09b8367793a9aca3db71" - "cc30c58179ec3e87c14c01d5c1f3434f" - "1d87", // 82 octets - 82 - ); - } - SECTION("Test case 3") { - TestHKDF("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "", - "", - "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", - 42 - ); - } - SECTION("Works with multiple of 32") { - // This generates exactly 64 bytes. Uses a 32 byte key and 4 byte salt as in EIP2333. - TestHKDF("8704f9ac024139fe62511375cf9bc534c0507dcf00c41603ac935cd5943ce0b4b88599390de14e743ca2f56a73a04eae13aa3f3b969b39d8701e0d69a6f8d42f", - "53d8e19b", - "", - "eb01c9cd916653df76ffa61b6ab8a74e254ebfd9bfc43e624cc12a72b0373dee", - "8faabea85fc0c64e7ca86217cdc6dcdc88551c3244d56719e630a3521063082c46455c2fd5483811f9520a748f0099c1dfcfa52c54e1c22b5cdf70efb0f3c676", - 64 - ); + SECTION("Test case 2") + { + TestHKDF( + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34" + "007208d5b887185865", + 42); + } + SECTION("Test case 2") + { + TestHKDF( + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f", // 80 octets + "0x606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", // 80 octets + "0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", // 80 octets + "0x06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc24" + "4", // 32 octets + "0xb11e398dc80327a1c8e7f78c596a4934" + "4f012eda2d4efad8a050cc4c19afa97c" + "59045a99cac7827271cb41c65e590e09" + "da3275600c2f09b8367793a9aca3db71" + "cc30c58179ec3e87c14c01d5c1f3434f" + "1d87", // 82 octets + 82); + } + SECTION("Test case 3") + { + TestHKDF( + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d" + "201395faa4b61a96c8", + 42); + } + SECTION("Works with multiple of 32") + { + // This generates exactly 64 bytes. Uses a 32 byte key and 4 byte salt + // as in EIP2333. + TestHKDF( + "8704f9ac024139fe62511375cf9bc534c0507dcf00c41603ac935cd5943ce0b4b8" + "8599390de14e743ca2f56a73a04eae13aa3f3b969b39d8701e0d69a6f8d42f", + "53d8e19b", + "", + "eb01c9cd916653df76ffa61b6ab8a74e254ebfd9bfc43e624cc12a72b0373dee", + "8faabea85fc0c64e7ca86217cdc6dcdc88551c3244d56719e630a3521063082c46" + "455c2fd5483811f9520a748f0099c1dfcfa52c54e1c22b5cdf70efb0f3c676", + 64); } } -void TestEIP2333(string seedHex, string masterSkHex, string childSkHex, uint32_t childIndex) { +void TestEIP2333( + string seedHex, + string masterSkHex, + string childSkHex, + uint32_t childIndex) +{ auto masterSk = Util::HexToBytes(masterSkHex); auto childSk = Util::HexToBytes(childSkHex); @@ -230,60 +280,73 @@ void TestEIP2333(string seedHex, string masterSkHex, string childSkHex, uint32_t REQUIRE(calculatedMaster.size() == 32); REQUIRE(calculatedChild.size() == 32); - for (int i=0; i<32; i++) { + for (int i = 0; i < 32; i++) { REQUIRE(calculatedMaster[i] == masterSk[i]); } - for (int i=0; i<32; i++) { + for (int i = 0; i < 32; i++) { REQUIRE(calculatedChild[i] == childSk[i]); } } -TEST_CASE("EIP-2333 hardened HD keys") { - // The comments in the test cases correspond to integers that are converted to - // bytes using python int.to_bytes(32, "big").hex(), since the EIP spec provides ints, but c++ - // does not support bigint by default - SECTION("EIP-2333 Test case 1"){ - TestEIP2333("3141592653589793238462643383279502884197169399375105820974944592", - // 36167147331491996618072159372207345412841461318189449162487002442599770291484 - "4ff5e145590ed7b71e577bb04032396d1619ff41cb4e350053ed2dce8d1efd1c", - // 41787458189896526028601807066547832426569899195138584349427756863968330588237 - "5c62dcf9654481292aafa3348f1d1b0017bbfb44d6881d26d2b17836b38f204d", - 3141592653 - ); - } - SECTION("EIP-2333 Test case 2"){ - TestEIP2333("0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00", - // 13904094584487173309420026178174172335998687531503061311232927109397516192843 - "1ebd704b86732c3f05f30563dee6189838e73998ebc9c209ccff422adee10c4b", - // 12482522899285304316694838079579801944734479969002030150864436005368716366140 - "1b98db8b24296038eae3f64c25d693a269ef1e4d7ae0f691c572a46cf3c0913c", - 4294967295 - ); - } - SECTION("EIP-2333 Test case 3"){ - TestEIP2333("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", - // 44010626067374404458092393860968061149521094673473131545188652121635313364506 - "614d21b10c0e4996ac0608e0e7452d5720d95d20fe03c59a3321000a42432e1a", - // 4011524214304750350566588165922015929937602165683407445189263506512578573606 - "08de7136e4afc56ae3ec03b20517d9c1232705a747f588fd17832f36ae337526", - 42 - ); - } - SECTION("EIP-2333 Test vector with intermediate values"){ - TestEIP2333("c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", - // 5399117110774477986698372024995405256382522670366369834617409486544348441851 - "0x0befcabff4a664461cc8f190cdd51c05621eb2837c71a1362df5b465a674ecfb", - // 11812940737387919040225825939013910852517748782307378293770044673328955938106 - "1a1de3346883401f1e3b2281be5774080edb8e5ebe6f776b0f7af9fea942553a", - 0 - ); +TEST_CASE("EIP-2333 hardened HD keys") +{ + // The comments in the test cases correspond to integers that are converted + // to bytes using python int.to_bytes(32, "big").hex(), since the EIP spec + // provides ints, but c++ does not support bigint by default + SECTION("EIP-2333 Test case 1") + { + TestEIP2333( + "3141592653589793238462643383279502884197169399375105820974944592", + // 36167147331491996618072159372207345412841461318189449162487002442599770291484 + "4ff5e145590ed7b71e577bb04032396d1619ff41cb4e350053ed2dce8d1efd1c", + // 41787458189896526028601807066547832426569899195138584349427756863968330588237 + "5c62dcf9654481292aafa3348f1d1b0017bbfb44d6881d26d2b17836b38f204d", + 3141592653); + } + SECTION("EIP-2333 Test case 2") + { + TestEIP2333( + "0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC0" + "0", + // 13904094584487173309420026178174172335998687531503061311232927109397516192843 + "1ebd704b86732c3f05f30563dee6189838e73998ebc9c209ccff422adee10c4b", + // 12482522899285304316694838079579801944734479969002030150864436005368716366140 + "1b98db8b24296038eae3f64c25d693a269ef1e4d7ae0f691c572a46cf3c0913c", + 4294967295); + } + SECTION("EIP-2333 Test case 3") + { + TestEIP2333( + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa" + "3", + // 44010626067374404458092393860968061149521094673473131545188652121635313364506 + "614d21b10c0e4996ac0608e0e7452d5720d95d20fe03c59a3321000a42432e1a", + // 4011524214304750350566588165922015929937602165683407445189263506512578573606 + "08de7136e4afc56ae3ec03b20517d9c1232705a747f588fd17832f36ae337526", + 42); + } + SECTION("EIP-2333 Test vector with intermediate values") + { + TestEIP2333( + "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f" + "09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", + // 5399117110774477986698372024995405256382522670366369834617409486544348441851 + "0x0befcabff4a664461cc8f190cdd51c05621eb2837c71a1362df5b465a674ecf" + "b", + // 11812940737387919040225825939013910852517748782307378293770044673328955938106 + "1a1de3346883401f1e3b2281be5774080edb8e5ebe6f776b0f7af9fea942553a", + 0); } } -TEST_CASE("Unhardened HD keys") { - SECTION("Should match derivation through private and public keys"){ - const vector seed = {1, 50, 6, 244, 24, 199, 1, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; +TEST_CASE("Unhardened HD keys") +{ + SECTION("Should match derivation through private and public keys") + { + const vector seed = {1, 50, 6, 244, 24, 199, 1, 25, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29}; PrivateKey sk = BasicSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); @@ -293,15 +356,20 @@ TEST_CASE("Unhardened HD keys") { REQUIRE(childSk.GetG1Element() == childPk); - PrivateKey grandchildSk = BasicSchemeMPL().DeriveChildSkUnhardened(childSk, 12142); - G1Element grandcihldPk = BasicSchemeMPL().DeriveChildPkUnhardened(childPk, 12142); + PrivateKey grandchildSk = + BasicSchemeMPL().DeriveChildSkUnhardened(childSk, 12142); + G1Element grandchildPk = + BasicSchemeMPL().DeriveChildPkUnhardened(childPk, 12142); - REQUIRE(grandchildSk.GetG1Element() == grandcihldPk); + REQUIRE(grandchildSk.GetG1Element() == grandchildPk); } - SECTION("Should derive public child from parent") { - const vector seed = {2, 50, 6, 244, 24, 199, 1, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + SECTION("Should derive public child from parent") + { + const vector seed = {2, 50, 6, 244, 24, 199, 1, 25, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29}; PrivateKey sk = BasicSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); @@ -316,10 +384,17 @@ TEST_CASE("Unhardened HD keys") { } } -TEST_CASE("IETF test vectors") { - SECTION ("Pyecc vector") { - string sig1BasicHex = "96ba34fac33c7f129d602a0bc8a3d43f9abc014eceaab7359146b4b150e57b808645738f35671e9e10e0d862a30cab70074eb5831d13e6a5b162d01eebe687d0164adbd0a864370a7c222a2768d7704da254f1bf1823665bc2361f9dd8c00e99"; - string sk = "0x0101010101010101010101010101010101010101010101010101010101010101"; +TEST_CASE("IETF test vectors") +{ + SECTION("Pyecc vector") + { + string sig1BasicHex = + "96ba34fac33c7f129d602a0bc8a3d43f9abc014eceaab7359146b4b150e57b8086" + "45738f35671e9e10e0d862a30cab70074eb5831d13e6a5b162d01eebe687d0164a" + "dbd0a864370a7c222a2768d7704da254f1bf1823665bc2361f9dd8c00e99"; + string sk = + "0x010101010101010101010101010101010101010101010101010101010101010" + "1"; vector msg = {3, 1, 4, 1, 5, 9}; auto skobj = PrivateKey::FromBytes(Bytes(Util::HexToBytes(sk))); G2Element sig = BasicSchemeMPL().Sign(skobj, msg); @@ -331,9 +406,10 @@ TEST_CASE("IETF test vectors") { } } - -TEST_CASE("Chia test vectors") { - SECTION("Chia test vectors 1 (Basic)") { +TEST_CASE("Chia test vectors") +{ + SECTION("Chia test vectors 1 (Basic)") + { vector seed1(32, 0x00); // All 0s vector seed2(32, 0x01); // All 1s vector message1 = {7, 8, 9}; @@ -343,7 +419,6 @@ TEST_CASE("Chia test vectors") { G1Element pk1 = sk1.GetG1Element(); G2Element sig1 = BasicSchemeMPL().Sign(sk1, message1); - PrivateKey sk2 = BasicSchemeMPL().KeyGen(seed2); G1Element pk2 = sk2.GetG1Element(); G2Element sig2 = BasicSchemeMPL().Sign(sk2, message2); @@ -366,20 +441,26 @@ TEST_CASE("Chia test vectors") { REQUIRE( Util::HexStr(sig2.Serialize()) == - "a9c4d3e689b82c7ec7e838dac2380cb014f9a08f6cd6ba044c263746e39a8f7a60ffee4afb7" - "8f146c2e421360784d58f0029491e3bd8ab84f0011d258471ba4e87059de295d9aba845c044e" + "a9c4d3e689b82c7ec7e838dac2380cb014f9a08f6cd6ba044c263746e39a8f7a60" + "ffee4afb7" + "8f146c2e421360784d58f0029491e3bd8ab84f0011d258471ba4e87059de295d9a" + "ba845c044e" "e83f6cf2411efd379ef38bf4cf41d5f3c0ae1205d"); G2Element aggSig1 = BasicSchemeMPL().Aggregate({sig1, sig2}); REQUIRE( Util::HexStr(aggSig1.Serialize()) == - "aee003c8cdaf3531b6b0ca354031b0819f7586b5846796615aee8108fec75ef838d181f9d24" - "4a94d195d7b0231d4afcf06f27f0cc4d3c72162545c240de7d5034a7ef3a2a03c0159de982fb" + "aee003c8cdaf3531b6b0ca354031b0819f7586b5846796615aee8108fec75ef838" + "d181f9d24" + "4a94d195d7b0231d4afcf06f27f0cc4d3c72162545c240de7d5034a7ef3a2a03c0" + "159de982fb" "c2e7790aeb455e27beae91d64e077c70b5506dea3"); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message1, message2}, aggSig1)); - REQUIRE(!BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message1, message2}, sig1)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message1, message2}, aggSig1)); + REQUIRE(!BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message1, message2}, sig1)); REQUIRE(!BasicSchemeMPL().Verify(pk1, message1, sig2)); REQUIRE(!BasicSchemeMPL().Verify(pk1, message2, sig1)); @@ -393,15 +474,21 @@ TEST_CASE("Chia test vectors") { G2Element aggSig2 = BasicSchemeMPL().Aggregate({sig3, sig4, sig5}); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk1, pk2}, vector>{message3, message4, message5}, aggSig2)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk1, pk2}, + vector>{message3, message4, message5}, + aggSig2)); REQUIRE( Util::HexStr(aggSig2.Serialize()) == - "a0b1378d518bea4d1100adbc7bdbc4ff64f2c219ed6395cd36fe5d2aa44a4b8e710b607afd9" - "65e505a5ac3283291b75413d09478ab4b5cfbafbeea366de2d0c0bcf61deddaa521f6020460f" + "a0b1378d518bea4d1100adbc7bdbc4ff64f2c219ed6395cd36fe5d2aa44a4b8e71" + "0b607afd9" + "65e505a5ac3283291b75413d09478ab4b5cfbafbeea366de2d0c0bcf61deddaa52" + "1f6020460f" "d547ab37659ae207968b545727beba0a3c5572b9c"); } - SECTION("Chia test vector 2 (Augmented, aggregate of aggregates)") { + SECTION("Chia test vector 2 (Augmented, aggregate of aggregates)") + { vector message1 = {1, 2, 3, 40}; vector message2 = {5, 6, 70, 201}; vector message3 = {9, 10, 11, 12, 13}; @@ -423,20 +510,26 @@ TEST_CASE("Chia test vectors") { G2Element sig5 = AugSchemeMPL().Sign(sk1, message1); G2Element sig6 = AugSchemeMPL().Sign(sk1, message4); - G2Element aggSigL = AugSchemeMPL().Aggregate({sig1, sig2}); G2Element aggSigR = AugSchemeMPL().Aggregate({sig3, sig4, sig5}); G2Element aggSig = AugSchemeMPL().Aggregate({aggSigL, aggSigR, sig6}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2, pk2, pk1, pk1, pk1}, vector>{message1, message2, message1, message3, message1, message4}, aggSig)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2, pk2, pk1, pk1, pk1}, + vector>{ + message1, message2, message1, message3, message1, message4}, + aggSig)); REQUIRE( Util::HexStr(aggSig.Serialize()) == - "a1d5360dcb418d33b29b90b912b4accde535cf0e52caf467a005dc632d9f7af44b6c4e9acd4" - "6eac218b28cdb07a3e3bc087df1cd1e3213aa4e11322a3ff3847bbba0b2fd19ddc25ca964871" + "a1d5360dcb418d33b29b90b912b4accde535cf0e52caf467a005dc632d9f7af44b" + "6c4e9acd4" + "6eac218b28cdb07a3e3bc087df1cd1e3213aa4e11322a3ff3847bbba0b2fd19ddc" + "25ca964871" "997b9bceeab37a4c2565876da19382ea32a962200"); } - SECTION("Chia test vector 3 (PoP)") { + SECTION("Chia test vector 3 (PoP)") + { vector message1 = {1, 2, 3, 40, 50}; vector seed1(32, 0x04); // All 4s @@ -446,13 +539,16 @@ TEST_CASE("Chia test vectors") { G2Element pop = PopSchemeMPL().PopProve(sk1); REQUIRE(PopSchemeMPL().PopVerify(sk1.GetG1Element(), pop)); - REQUIRE(Util::HexStr(pop.Serialize()) == "84f709159435f0dc73b3e8bf6c78d85282d19231555a8ee3b6e2573aaf66872d9203fefa1ef" - "700e34e7c3f3fb28210100558c6871c53f1ef6055b9f06b0d1abe22ad584ad3b957f3018a8f5" - "8227c6c716b1e15791459850f2289168fa0cf9115"); + REQUIRE( + Util::HexStr(pop.Serialize()) == + "84f709159435f0dc73b3e8bf6c78d85282d19231555a8ee3b6e2573aaf66872d92" + "03fefa1ef" + "700e34e7c3f3fb28210100558c6871c53f1ef6055b9f06b0d1abe22ad584ad3b95" + "7f3018a8f5" + "8227c6c716b1e15791459850f2289168fa0cf9115"); } } - TEST_CASE("Key generation") { SECTION("Should generate a keypair from a seed") @@ -463,12 +559,10 @@ TEST_CASE("Key generation") REQUIRE_THROWS(BasicSchemeMPL().KeyGen(seed1)); PrivateKey sk = BasicSchemeMPL().KeyGen(seed2); G1Element pk = sk.GetG1Element(); - REQUIRE(core_get()->code == RLC_OK); REQUIRE(pk.GetFingerprint() == 0x8ee7ba56); } } - TEST_CASE("Error handling") { SECTION("Should throw on a bad private key") @@ -478,7 +572,8 @@ TEST_CASE("Error handling") uint8_t* skData = Util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); skData[0] = 255; - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE))); + REQUIRE_THROWS( + PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE))); Util::SecFree(skData); } @@ -487,7 +582,8 @@ TEST_CASE("Error handling") vector buf(G1Element::SIZE, 0); for (int i = 0; i < 0xFF; i++) { buf[0] = (uint8_t)i; - if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have only zero values + if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have + // only zero values REQUIRE_NOTHROW(G1Element::FromByteVector(buf)); } else { REQUIRE_THROWS(G1Element::FromByteVector(buf)); @@ -500,47 +596,20 @@ TEST_CASE("Error handling") vector buf(G2Element::SIZE, 0); for (int i = 0; i < 0xFF; i++) { buf[0] = (uint8_t)i; - if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have only zero values + if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have + // only zero values REQUIRE_NOTHROW(G2Element::FromByteVector(buf)); } else { REQUIRE_THROWS(G2Element::FromByteVector(buf)); } } - // Trigger "G2 element must always have 48th byte start with 0b000" error case + // Trigger "G2 element must always have 48th byte start with 0b000" + // error case buf[48] = 0xFF; REQUIRE_THROWS(G2Element::FromByteVector(buf)); } - - SECTION("Error handling should be thread safe") - { - core_get()->code = 10; - REQUIRE(core_get()->code == 10); - - ctx_t* ctx1 = core_get(); - - // spawn a thread and make sure it uses a different/same context depending on relic's multithreading setup - std::thread([&]() { -#if MULTI != RELIC_NONE - REQUIRE(ctx1 != core_get()); - REQUIRE(core_get()->code == RLC_OK); -#else - REQUIRE(ctx1 == core_get()); - REQUIRE(core_get()->code != RLC_OK); -#endif - core_get()->code = 1; - }).join(); - -#if MULTI != RELIC_NONE - REQUIRE(core_get()->code == 10); -#else - REQUIRE(core_get()->code == 1); -#endif - // reset so that future test cases don't fail - core_get()->code = RLC_OK; - } } - TEST_CASE("Util tests") { SECTION("Should convert an int to four bytes") @@ -558,7 +627,6 @@ TEST_CASE("Util tests") } } - TEST_CASE("Signature tests") { SECTION("Should use copy constructor") @@ -572,7 +640,8 @@ TEST_CASE("Signature tests") uint8_t skBytes[PrivateKey::PRIVATE_KEY_SIZE]; sk2.Serialize(skBytes); - PrivateKey sk4 = PrivateKey::FromBytes(Bytes(skBytes, PrivateKey::PRIVATE_KEY_SIZE)); + PrivateKey sk4 = + PrivateKey::FromBytes(Bytes(skBytes, PrivateKey::PRIVATE_KEY_SIZE)); G1Element pk2 = G1Element(pk1); G2Element sig1 = BasicSchemeMPL().Sign(sk4, message1); @@ -581,7 +650,8 @@ TEST_CASE("Signature tests") REQUIRE(BasicSchemeMPL().Verify(pk2, message1, sig2)); } - SECTION("Should sign with the zero key") { + SECTION("Should sign with the zero key") + { vector sk0(32, 0); PrivateKey sk = PrivateKey::FromByteVector(sk0); REQUIRE(sk.GetG1Element() == G1Element()); // Infinity @@ -632,7 +702,8 @@ TEST_CASE("Signature tests") uint8_t* skData = Util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); - PrivateKey sk2 = PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE)); + PrivateKey sk2 = + PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE)); REQUIRE(sk1 == sk2); auto pkData = pk1.Serialize(); @@ -654,7 +725,6 @@ TEST_CASE("Signature tests") SECTION("Should not verify aggregate with same message under BasicScheme") { vector message = {100, 2, 254, 88, 90, 45, 23}; - uint8_t hash[BLS::MESSAGE_HASH_LEN]; vector seed(32, 0x50); vector seed2(32, 0x70); @@ -669,13 +739,17 @@ TEST_CASE("Signature tests") G2Element sig2 = BasicSchemeMPL().Sign(sk2, message); G2Element aggSig = BasicSchemeMPL().Aggregate({sig1, sig2}); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig) == false); } - SECTION("Should verify aggregate with same message under AugScheme/PopScheme") + SECTION( + "Should verify aggregate with same message under AugScheme/PopScheme") { vector message = {100, 2, 254, 88, 90, 45, 23}; - uint8_t hash[BLS::MESSAGE_HASH_LEN]; vector seed(32, 0x50); vector seed2(32, 0x70); @@ -689,19 +763,21 @@ TEST_CASE("Signature tests") G2Element sig1Aug = AugSchemeMPL().Sign(sk1, message); G2Element sig2Aug = AugSchemeMPL().Sign(sk2, message); G2Element aggSigAug = AugSchemeMPL().Aggregate({sig1Aug, sig2Aug}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSigAug)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message}, aggSigAug)); G2Element sig1Pop = PopSchemeMPL().Sign(sk1, message); G2Element sig2Pop = PopSchemeMPL().Sign(sk2, message); G2Element aggSigPop = PopSchemeMPL().Aggregate({sig1Pop, sig2Pop}); - REQUIRE(PopSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSigPop)); + REQUIRE(PopSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message}, aggSigPop)); } SECTION("Should Aug aggregate many G2Elements, diff message") { vector pks; vector sigs; - vector > ms; + vector> ms; for (uint8_t i = 0; i < 80; i++) { vector message = {0, 100, 2, 45, 64, 12, 12, 63, i}; @@ -720,8 +796,8 @@ TEST_CASE("Signature tests") SECTION("Aggregate Verification of zero items with infinity should pass") { vector pks_as_g1; - vector > pks_as_bytes; - vector > msgs; + vector> pks_as_bytes; + vector> msgs; vector sigs; sigs.push_back(G2Element()); @@ -731,17 +807,23 @@ TEST_CASE("Signature tests") REQUIRE(aggSig == G2Element()); REQUIRE(AugSchemeMPL().AggregateVerify(pks_as_g1, msgs, aggSig)); - REQUIRE(AugSchemeMPL().AggregateVerify(pks_as_bytes, msgs, aggSig.Serialize())); + REQUIRE(AugSchemeMPL().AggregateVerify( + pks_as_bytes, msgs, aggSig.Serialize())); REQUIRE(BasicSchemeMPL().AggregateVerify(pks_as_g1, msgs, aggSig)); - REQUIRE(BasicSchemeMPL().AggregateVerify(pks_as_bytes, msgs, aggSig.Serialize())); + REQUIRE(BasicSchemeMPL().AggregateVerify( + pks_as_bytes, msgs, aggSig.Serialize())); - // FastAggregateVerify takes one message, and requires at least one key + // FastAggregateVerify takes one message, and requires at least one key vector msg; REQUIRE(pks_as_g1.size() == 0); - REQUIRE(PopSchemeMPL().FastAggregateVerify(pks_as_g1, msg, aggSig) == false); + REQUIRE( + PopSchemeMPL().FastAggregateVerify(pks_as_g1, msg, aggSig) == + false); REQUIRE(pks_as_bytes.size() == 0); - REQUIRE(PopSchemeMPL().FastAggregateVerify(pks_as_bytes, msg, aggSig.Serialize()) == false); + REQUIRE( + PopSchemeMPL().FastAggregateVerify( + pks_as_bytes, msg, aggSig.Serialize()) == false); } SECTION("Verification with identity pubkey or signature should fail") @@ -765,9 +847,25 @@ TEST_CASE("Signature tests") pks = {pk}; REQUIRE(BasicSchemeMPL().AggregateVerify(pks, msgs_b, G2Element()) == false); } + SECTION("Aggregate same sig element") + { + vector message = {100, 2, 254, 88, 90, 45, 23}; + + vector seed(32, 0x50); + + PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); + + G1Element pk1 = sk1.GetG1Element(); + + G2Element sig1Aug = AugSchemeMPL().Sign(sk1, message); + G2Element aggSigAug = AugSchemeMPL().Aggregate({sig1Aug, sig1Aug}); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk1}, vector>{message, message}, aggSigAug)); + } } -TEST_CASE("Agg sks") { +TEST_CASE("Agg sks") +{ SECTION("Should create aggregates with agg sk (basic scheme)") { const vector message = {100, 2, 254, 88, 90, 45, 23}; @@ -792,7 +890,6 @@ TEST_CASE("Agg sks") { const G2Element aggSig2 = BasicSchemeMPL().Sign(aggSk, message); - const G2Element aggSig = BasicSchemeMPL().Aggregate({sig1, sig2}); REQUIRE(aggSig == aggSig2); @@ -801,8 +898,16 @@ TEST_CASE("Agg sks") { REQUIRE(BasicSchemeMPL().Verify(aggPubKey, message, aggSig2)); // Verify aggregate with both keys (Fails since not distinct) - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig) == false); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig2) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig2) == false); // Try the same with distinct message, and same sk vector message2 = {200, 29, 54, 8, 9, 29, 155, 55}; @@ -824,12 +929,17 @@ TEST_CASE("Agg sks") { REQUIRE(pkFinal != aggPubKey); // Cannot verify with aggPubKey (since we have multiple messages) - REQUIRE(BasicSchemeMPL().AggregateVerify({aggPubKey, pk2}, vector>{message, message2}, aggSigFinal)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {aggPubKey, pk2}, + vector>{message, message2}, + aggSigFinal)); } } -TEST_CASE("Advanced") { - SECTION("Should aggregate with multiple levels, degenerate") { +TEST_CASE("Advanced") +{ + SECTION("Should aggregate with multiple levels, degenerate") + { vector message1 = {100, 2, 254, 88, 90, 45, 23}; PrivateKey sk1 = AugSchemeMPL().KeyGen(getRandomSeed()); G1Element pk1 = sk1.GetG1Element(); @@ -848,7 +958,8 @@ TEST_CASE("Advanced") { REQUIRE(AugSchemeMPL().AggregateVerify(pks, ms, aggSig)); } - SECTION("Should aggregate with multiple levels, different messages") { + SECTION("Should aggregate with multiple levels, different messages") + { vector message1 = {100, 2, 254, 88, 90, 45, 23}; vector message2 = {192, 29, 2, 0, 0, 45, 23}; vector message3 = {52, 29, 2, 0, 0, 45, 102}; @@ -886,25 +997,24 @@ TEST_CASE("Advanced") { SECTION("README") { // Example seed, used to generate private key. Always use - // a secure RNG with sufficient entropy to generate a seed (at least 32 bytes). - vector seed = {0, 50, 6, 244, 24, 199, 1, 25, 52, 88, 192, - 19, 18, 12, 89, 6, 220, 18, 102, 58, 209, 82, - 12, 62, 89, 110, 182, 9, 44, 20, 254, 22}; + // a secure RNG with sufficient entropy to generate a seed (at least 32 + // bytes). + vector seed = {0, 50, 6, 244, 24, 199, 1, 25, + 52, 88, 192, 19, 18, 12, 89, 6, + 220, 18, 102, 58, 209, 82, 12, 62, + 89, 110, 182, 9, 44, 20, 254, 22}; PrivateKey sk = AugSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); - vector message = {1, 2, 3, 4, 5}; // Message is passed in as a byte vector + vector message = { + 1, 2, 3, 4, 5}; // Message is passed in as a byte vector G2Element signature = AugSchemeMPL().Sign(sk, message); vector skBytes = sk.Serialize(); vector pkBytes = pk.Serialize(); vector signatureBytes = signature.Serialize(); - cout << Util::HexStr(skBytes) << endl; // 32 bytes - cout << Util::HexStr(pkBytes) << endl; // 48 bytes - cout << Util::HexStr(signatureBytes) << endl; // 96 bytes - // Takes array of 32 bytes PrivateKey skc = PrivateKey::FromByteVector(skBytes); @@ -934,7 +1044,8 @@ TEST_CASE("Advanced") { // Signatures can be noninteractively combined by anyone G2Element aggSig = AugSchemeMPL().Aggregate({sig1, sig2}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message2}, aggSig)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message2}, aggSig)); seed[0] = 3; PrivateKey sk3 = AugSchemeMPL().KeyGen(seed); @@ -942,14 +1053,17 @@ TEST_CASE("Advanced") { vector message3 = {100, 2, 254, 88, 90, 45, 23}; G2Element sig3 = AugSchemeMPL().Sign(sk3, message3); - // Arbitrary trees of aggregates G2Element aggSigFinal = AugSchemeMPL().Aggregate({aggSig, sig3}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2, pk3}, vector>{message, message2, message3}, aggSigFinal)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2, pk3}, + vector>{message, message2, message3}, + aggSigFinal)); - // If the same message is signed, you can use Proof of Posession (PopScheme) for efficiency - // A proof of possession MUST be passed around with the PK to ensure security. + // If the same message is signed, you can use Proof of Posession + // (PopScheme) for efficiency A proof of possession MUST be passed + // around with the PK to ensure security. G2Element popSig1 = PopSchemeMPL().Sign(sk1, message); G2Element popSig2 = PopSchemeMPL().Sign(sk2, message); @@ -961,9 +1075,11 @@ TEST_CASE("Advanced") { REQUIRE(PopSchemeMPL().PopVerify(pk1, pop1)); REQUIRE(PopSchemeMPL().PopVerify(pk2, pop2)); REQUIRE(PopSchemeMPL().PopVerify(pk3, pop3)); - G2Element popSigAgg = PopSchemeMPL().Aggregate({popSig1, popSig2, popSig3}); + G2Element popSigAgg = + PopSchemeMPL().Aggregate({popSig1, popSig2, popSig3}); - REQUIRE(PopSchemeMPL().FastAggregateVerify({pk1, pk2, pk3}, message, popSigAgg)); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + {pk1, pk2, pk3}, message, popSigAgg)); // Aggregate public key, indistinguishable from a single public key G1Element popAggPk = pk1 + pk2 + pk3; @@ -973,17 +1089,20 @@ TEST_CASE("Advanced") { PrivateKey aggSk = PrivateKey::Aggregate({sk1, sk2, sk3}); REQUIRE(PopSchemeMPL().Sign(aggSk, message) == popSigAgg); - PrivateKey masterSk = AugSchemeMPL().KeyGen(seed); PrivateKey child = AugSchemeMPL().DeriveChildSk(masterSk, 152); PrivateKey grandchild = AugSchemeMPL().DeriveChildSk(child, 952); G1Element masterPk = masterSk.GetG1Element(); - PrivateKey childU = AugSchemeMPL().DeriveChildSkUnhardened(masterSk, 22); - PrivateKey grandchildU = AugSchemeMPL().DeriveChildSkUnhardened(childU, 0); + PrivateKey childU = + AugSchemeMPL().DeriveChildSkUnhardened(masterSk, 22); + PrivateKey grandchildU = + AugSchemeMPL().DeriveChildSkUnhardened(childU, 0); - G1Element childUPk = AugSchemeMPL().DeriveChildPkUnhardened(masterPk, 22); - G1Element grandchildUPk = AugSchemeMPL().DeriveChildPkUnhardened(childUPk, 0); + G1Element childUPk = + AugSchemeMPL().DeriveChildPkUnhardened(masterPk, 22); + G1Element grandchildUPk = + AugSchemeMPL().DeriveChildPkUnhardened(childUPk, 0); REQUIRE(grandchildUPk == grandchildU.GetG1Element()); } @@ -1005,12 +1124,16 @@ TEST_CASE("Advanced") { G2Element g2Bytes = G2Element::FromBytes(Bytes(vecG2Element)); REQUIRE(g2Vector == g2Bytes); - G1Element g1MessageVector = G1Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); - G1Element g1MessageBytes = G1Element::FromMessage(Bytes(vecHash), vecHash.data(), vecHash.size()); + G1Element g1MessageVector = + G1Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); + G1Element g1MessageBytes = G1Element::FromMessage( + Bytes(vecHash), vecHash.data(), vecHash.size()); REQUIRE(g1MessageVector == g1MessageBytes); - G2Element g2MessageVector = G2Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); - G2Element g2MessageBytes = G2Element::FromMessage(Bytes(vecHash), vecHash.data(), vecHash.size()); + G2Element g2MessageVector = + G2Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); + G2Element g2MessageBytes = G2Element::FromMessage( + Bytes(vecHash), vecHash.data(), vecHash.size()); REQUIRE(g2MessageVector == g2MessageBytes); G1Element g1_1 = pk1.GetG1Element(); @@ -1018,29 +1141,41 @@ TEST_CASE("Advanced") { G1Element g1_3 = pk3.GetG1Element(); G2Element g2BasicSignVector1 = BasicSchemeMPL().Sign(pk1, vecHash); - G2Element g2BasicSignBytes1 = BasicSchemeMPL().Sign(pk1, Bytes(vecHash)); + G2Element g2BasicSignBytes1 = + BasicSchemeMPL().Sign(pk1, Bytes(vecHash)); REQUIRE(g2BasicSignVector1 == g2BasicSignBytes1); G2Element g2BasicSign2 = BasicSchemeMPL().Sign(pk2, Bytes(vecHash)); - G2Element g2BasicSign3 = BasicSchemeMPL().Sign(pk3, Bytes(vecG2Element)); + G2Element g2BasicSign3 = + BasicSchemeMPL().Sign(pk3, Bytes(vecG2Element)); REQUIRE(g2BasicSignVector1 != g2BasicSign2); - REQUIRE(BasicSchemeMPL().Verify(Bytes(g1_1.Serialize()), Bytes(vecHash), Bytes(g2BasicSignVector1.Serialize()))); - REQUIRE(BasicSchemeMPL().Verify(g1_1, Bytes(vecHash), g2BasicSignVector1)); + REQUIRE(BasicSchemeMPL().Verify( + Bytes(g1_1.Serialize()), + Bytes(vecHash), + Bytes(g2BasicSignVector1.Serialize()))); + REQUIRE( + BasicSchemeMPL().Verify(g1_1, Bytes(vecHash), g2BasicSignVector1)); - vector> vecG1Vector = {g1_1.Serialize(), g1_3.Serialize()}; - vector> vecG2Vector = {g2BasicSignVector1.Serialize(), g2BasicSign3.Serialize()}; + vector> vecG1Vector = { + g1_1.Serialize(), g1_3.Serialize()}; + vector> vecG2Vector = { + g2BasicSignVector1.Serialize(), g2BasicSign3.Serialize()}; vector> vecHashes = {vecHash, vecG2Element}; auto aggVector = BasicSchemeMPL().Aggregate(vecG2Vector); - auto aggBytes = BasicSchemeMPL().Aggregate(vector{vecG2Vector.begin(), vecG2Vector.end()}); + auto aggBytes = BasicSchemeMPL().Aggregate( + vector{vecG2Vector.begin(), vecG2Vector.end()}); REQUIRE(aggVector == aggBytes); - REQUIRE(BasicSchemeMPL().AggregateVerify(vector{vecG1Vector.begin(), vecG1Vector.end()}, - vector{vecHashes.begin(), vecHashes.end()}, - Bytes(aggVector))); - REQUIRE(BasicSchemeMPL().AggregateVerify({g1_1, g1_3}, - vector{vecHashes.begin(), vecHashes.end()}, - G2Element::FromBytes(aggVector))); + + REQUIRE(BasicSchemeMPL().AggregateVerify( + vector{vecG1Vector.begin(), vecG1Vector.end()}, + vector{vecHashes.begin(), vecHashes.end()}, + Bytes(aggVector))); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {g1_1, g1_3}, + vector{vecHashes.begin(), vecHashes.end()}, + G2Element::FromBytes(aggVector))); G2Element g2AugSignVector1 = AugSchemeMPL().Sign(pk1, vecHash); G2Element g2AugSignBytes1 = AugSchemeMPL().Sign(pk1, Bytes(vecHash)); @@ -1048,40 +1183,53 @@ TEST_CASE("Advanced") { REQUIRE(g2AugSignVector1 == g2AugSignBytes1); REQUIRE(g2AugSignVector1 != g2AugSign2); - REQUIRE(AugSchemeMPL().Verify(Bytes(g1_1.Serialize()), Bytes(vecHash), Bytes(g2AugSignVector1.Serialize()))); + REQUIRE(AugSchemeMPL().Verify( + Bytes(g1_1.Serialize()), + Bytes(vecHash), + Bytes(g2AugSignVector1.Serialize()))); REQUIRE(AugSchemeMPL().Verify(g1_1, Bytes(vecHash), g2AugSignVector1)); - vector> vecG1AugVector = {g1_1.Serialize(), g1_2.Serialize()}; - vector> vecG2AugVector = {g2AugSignVector1.Serialize(), g2AugSign2.Serialize()}; + vector> vecG1AugVector = { + g1_1.Serialize(), g1_2.Serialize()}; + vector> vecG2AugVector = { + g2AugSignVector1.Serialize(), g2AugSign2.Serialize()}; auto aggAugVector = AugSchemeMPL().Aggregate(vecG2AugVector); - auto aggAugBytes = AugSchemeMPL().Aggregate(vector{vecG2AugVector.begin(), vecG2AugVector.end()}); + auto aggAugBytes = AugSchemeMPL().Aggregate( + vector{vecG2AugVector.begin(), vecG2AugVector.end()}); REQUIRE(aggAugVector == aggAugBytes); - REQUIRE(AugSchemeMPL().AggregateVerify(vector{vecG1AugVector.begin(), vecG1AugVector.end()}, - vector{vecHashes.begin(), vecHashes.end()}, - Bytes(aggAugVector))); - REQUIRE(AugSchemeMPL().AggregateVerify({g1_1, g1_2}, - vector{vecHashes.begin(), vecHashes.end()}, - G2Element::FromBytes(aggAugVector))); + REQUIRE(AugSchemeMPL().AggregateVerify( + vector{vecG1AugVector.begin(), vecG1AugVector.end()}, + vector{vecHashes.begin(), vecHashes.end()}, + Bytes(aggAugVector))); + REQUIRE(AugSchemeMPL().AggregateVerify( + {g1_1, g1_2}, + vector{vecHashes.begin(), vecHashes.end()}, + G2Element::FromBytes(aggAugVector))); G2Element proof = PopSchemeMPL().PopProve(pk1); REQUIRE(PopSchemeMPL().PopVerify(g1_1, proof)); - REQUIRE(PopSchemeMPL().PopVerify(Bytes(g1_1.Serialize()), Bytes(proof.Serialize()))); + REQUIRE(PopSchemeMPL().PopVerify( + Bytes(g1_1.Serialize()), Bytes(proof.Serialize()))); G2Element g2Pop1 = PopSchemeMPL().Sign(pk1, vecHash); G2Element g2Pop2 = PopSchemeMPL().Sign(pk2, vecHash); G2Element g2PopAgg = PopSchemeMPL().Aggregate({g2Pop1, g2Pop2}); vecG1Vector = {g1_1.Serialize(), g1_2.Serialize()}; - REQUIRE(PopSchemeMPL().FastAggregateVerify({g1_1, g1_2}, Bytes(vecHash), g2PopAgg)); - REQUIRE(PopSchemeMPL().FastAggregateVerify(vector{vecG1Vector.begin(), vecG1Vector.end()}, - Bytes(vecHash), Bytes(g2PopAgg.Serialize()))); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + {g1_1, g1_2}, Bytes(vecHash), g2PopAgg)); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + vector{vecG1Vector.begin(), vecG1Vector.end()}, + Bytes(vecHash), + Bytes(g2PopAgg.Serialize()))); } } - -TEST_CASE("Schemes") { - SECTION("Basic Scheme") { +TEST_CASE("Schemes") +{ + SECTION("Basic Scheme") + { vector seed1(32, 0x04); vector seed2(32, 0x05); std::array msg1 = {7, 8, 9}; @@ -1094,7 +1242,6 @@ TEST_CASE("Schemes") { G2Element sig1 = BasicSchemeMPL().Sign(sk1, msg1); vector sig1v = BasicSchemeMPL().Sign(sk1, msg1).Serialize(); - REQUIRE(BasicSchemeMPL().Verify(pk1v, msg1, sig1v)); PrivateKey sk2 = BasicSchemeMPL().KeyGen(seed2); @@ -1114,7 +1261,8 @@ TEST_CASE("Schemes") { REQUIRE(BasicSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = BasicSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = BasicSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + BasicSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(BasicSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, aggsigv)); } @@ -1153,7 +1301,8 @@ TEST_CASE("Schemes") { REQUIRE(AugSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = AugSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = AugSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + AugSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(AugSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, Bytes(aggsigv.data(), aggsigv.size()))); } @@ -1192,7 +1341,8 @@ TEST_CASE("Schemes") { REQUIRE(PopSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = PopSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = PopSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + PopSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(PopSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(PopSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, Bytes(aggsigv.data(), aggsigv.size()))); @@ -1207,8 +1357,8 @@ TEST_CASE("Schemes") { G2Element sig2_same = PopSchemeMPL().Sign(sk2, msg1); vector sig2v_same = PopSchemeMPL().Sign(sk2, msg1).Serialize(); G2Element aggsig_same = PopSchemeMPL().Aggregate({sig1, sig2_same}); - auto aggsigv_same = - PopSchemeMPL().Aggregate(vector>{sig1v, sig2v_same}); + auto aggsigv_same = PopSchemeMPL().Aggregate( + vector>{sig1v, sig2v_same}); REQUIRE( PopSchemeMPL().FastAggregateVerify({pk1, pk2}, msg1, aggsig_same)); REQUIRE(PopSchemeMPL().FastAggregateVerify( @@ -1266,8 +1416,7 @@ TEST_CASE("Schemes") { legacySigs.push_back(legacyScheme.Sign(sk, message)); } - G2Element legacySecureAggSig; - REQUIRE_NOTHROW(legacySecureAggSig = legacyScheme.AggregateSecure(legacyPks, legacySigs, message)); + G2Element legacySecureAggSig = legacyScheme.AggregateSecure(legacyPks, legacySigs, message); REQUIRE(legacyScheme.VerifySecure(legacyPks, legacySecureAggSig, message)); } @@ -1389,9 +1538,11 @@ TEST_CASE("Legacy HD keys") { .GetPublicKey(); REQUIRE(sk3.GetG1Element() == pk4); - G2Element sig = LegacySchemeMPL().Sign(sk3, Bytes(seed)); + // the legacy scheme signs 32-byte message hashes + std::vector hash(32, 0x42); + G2Element sig = LegacySchemeMPL().Sign(sk3, Bytes(hash)); - REQUIRE(LegacySchemeMPL().Verify(sk3.GetG1Element(), Bytes(seed), sig)); + REQUIRE(LegacySchemeMPL().Verify(sk3.GetG1Element(), Bytes(hash), sig)); } SECTION("Should prevent hardened pk derivation") { @@ -1423,7 +1574,8 @@ TEST_CASE("Legacy HD keys") { cout << epk.GetPublicKey() << endl; cout << epk.GetChainCode() << endl; - G2Element sig1 = LegacySchemeMPL().Sign(esk.GetPrivateKey(), Bytes(seed)); + std::vector hash(32, 0x42); + G2Element sig1 = LegacySchemeMPL().Sign(esk.GetPrivateKey(), Bytes(hash)); cout << sig1 << endl; } @@ -1539,7 +1691,8 @@ TEST_CASE("Threshold Signatures") { TEST_CASE("CheckValid") { - SECTION("Valid points should succeed") { + SECTION("Valid points should succeed") + { vector seed(32, 0x05); vector msg1 = {10, 11, 12}; @@ -1561,32 +1714,36 @@ TEST_CASE("CheckValid") G1Element::FromBytes(Bytes(Util::HexToBytes(badPointHex)))); // FromBytesUnchecked does not throw - G1Element pk = + G1Element bad_pk = G1Element::FromBytesUnchecked(Bytes(Util::HexToBytes(badPointHex))); - REQUIRE(pk.IsValid() == false); - REQUIRE_THROWS(pk.CheckValid()); + REQUIRE(bad_pk.IsValid() == false); + REQUIRE_THROWS(bad_pk.CheckValid()); vector seed(32, 0x05); vector msg1 = {10, 11, 12}; PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); - G1Element pk1 = BasicSchemeMPL().SkToG1(sk1); + G1Element good_pk = BasicSchemeMPL().SkToG1(sk1); + REQUIRE(good_pk.IsValid() == true); G2Element sig1 = AugSchemeMPL().Sign(sk1, msg1); - REQUIRE(AugSchemeMPL().Verify(pk, msg1, sig1) == false); + REQUIRE(AugSchemeMPL().Verify(bad_pk, msg1, sig1) == false); + REQUIRE(AugSchemeMPL().Verify(good_pk, msg1, sig1) == true); } - SECTION("Invalid G2 points should not succeed") { - g2_t point_native; - g2_set_infty(point_native); - fp2_rand(point_native->x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + SECTION("Invalid G2 points should not succeed") + { + blst_p2 point_native; + memset(&point_native, 0, sizeof(blst_p2)); + + // fixed garbage coordinates: not on the curve and not infinity + memset(&(point_native.x), 0x11, sizeof(point_native.x)); + memset(&(point_native.y), 0x22, sizeof(point_native.y)); + memset(&(point_native.z), 0x33, sizeof(point_native.z)); G2Element point = G2Element::FromNative(point_native); REQUIRE(point.IsValid() == false); REQUIRE_THROWS(point.CheckValid()); auto badSer = point.Serialize(); - std::cout <x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + // Build an invalid G2 element from native coordinates: + // (-x, y) of the generator: not on the curve + blst_p2 point_native = *blst_p2_generator(); + blst_fp2_cneg(&point_native.x, &point_native.x, true); G2Element badSig = G2Element::FromNative(point_native); REQUIRE(badSig.IsValid() == false); @@ -1910,11 +2065,9 @@ TEST_CASE("FastAggregateVerify rejects identity elements") SECTION("Malformed non-identity signature rejected") { - g2_t point_native; - g2_set_infty(point_native); - fp2_rand(point_native->x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + // (-x, y) of the generator: not on the curve + blst_p2 point_native = *blst_p2_generator(); + blst_fp2_cneg(&point_native.x, &point_native.x, true); G2Element badSig = G2Element::FromNative(point_native); REQUIRE(badSig.IsValid() == false); @@ -1926,6 +2079,99 @@ TEST_CASE("FastAggregateVerify rejects identity elements") } } +TEST_CASE("G1 zeros-only check masks the flag bits of the first byte") +{ + // 0x85 || 47 zero bytes encodes x = 5 * 2^376, not an all-zero payload. + // relic decompressed it to an on-curve point outside the subgroup, so the + // unchecked decoder must do the same and the checked one must reject it + // for that reason, as the G2 decoder already does. + std::vector in(48, 0); + in[0] = 0x85; + G1Element u = G1Element::FromBytesUnchecked(Bytes(in)); + REQUIRE(!u.IsValid()); + REQUIRE(u.Serialize() == in); + REQUIRE_THROWS(G1Element::FromBytes(Bytes(in))); +} + +TEST_CASE("IsValid rejects points off the curve") +{ + // (4x, 8y, z) of the generator is not on the curve but passes blst's + // subgroup test, which assumes a curve point + uint8_t be[48] = {0}; + blst_fp four, eight; + be[47] = 4; + blst_fp_from_bendian(&four, be); + be[47] = 8; + blst_fp_from_bendian(&eight, be); + + blst_p1 off1 = *blst_p1_generator(); + blst_fp_mul(&off1.x, &off1.x, &four); + blst_fp_mul(&off1.y, &off1.y, &eight); + REQUIRE(!G1Element::FromNative(off1).IsValid()); + + blst_p2 off2 = *blst_p2_generator(); + blst_fp2 four2, eight2; + memset(&four2, 0, sizeof(four2)); + memset(&eight2, 0, sizeof(eight2)); + four2.fp[0] = four; + eight2.fp[0] = eight; + blst_fp2_mul(&off2.x, &off2.x, &four2); + blst_fp2_mul(&off2.y, &off2.y, &eight2); + REQUIRE(!G2Element::FromNative(off2).IsValid()); +} + +TEST_CASE("Degenerate legacy signatures do not verify") +{ + // A legacy G2 decode of x = (0, 0) with no square root leaves the point + // (0, 0, 1): not infinity, but (0, 0) in affine form. IsValid() rejects + // it before the pairing product. + std::vector h(32, 0x11); + G1Element g = G1Element::Generator(); + std::vector pp(96, 0); // c0 = p, c1 = p, both read as zero + const std::vector p = Util::HexToBytes( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); + std::copy(p.begin(), p.end(), pp.begin()); + std::copy(p.begin(), p.end(), pp.begin() + 48); + for (const std::vector& in : {std::vector(96, 0), pp}) { + G2Element bad = G2Element::FromBytes(Bytes(in), true); + REQUIRE(!LegacySchemeMPL().Verify(G1Element(), Bytes(h), bad)); + REQUIRE(!LegacySchemeMPL().Verify(g, Bytes(h), bad)); + std::vector pks{g, g.Negate()}; + std::vector msgs{Bytes(h), Bytes(h)}; + REQUIRE(!LegacySchemeMPL().AggregateVerify(pks, msgs, bad)); + } +} + +TEST_CASE("Legacy verification rejects messages that are not 32 bytes") +{ + PrivateKey sk = BasicSchemeMPL().KeyGen(std::vector(32, 0x55)); + std::vector h(32, 0x11); + G2Element sig = LegacySchemeMPL().Sign(sk, Bytes(h)); + REQUIRE(LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h), sig)); + std::vector h31(31, 0x11), h33(33, 0x11); + REQUIRE(!LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h31), sig)); + REQUIRE(!LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h33), sig)); + REQUIRE_THROWS(LegacySchemeMPL().Sign(sk, Bytes(h31))); +} + +TEST_CASE("Secure aggregation rejects an infinity public key") +{ + BasicSchemeMPL basic; + vector seed1(32, 0x11); + vector seed2(32, 0x22); + vector msg(32, 0x33); + PrivateKey sk1 = basic.KeyGen(seed1); + PrivateKey sk2 = basic.KeyGen(seed2); + vector pks = {sk1.GetG1Element(), sk2.GetG1Element()}; + vector sigs = {basic.Sign(sk1, msg), basic.Sign(sk2, msg)}; + + // positive control first, then only the second key changes + G2Element agg = basic.AggregateSecure(pks, sigs, Bytes(msg)); + REQUIRE(basic.VerifySecure(pks, agg, Bytes(msg))); + pks[1] = G1Element(); + REQUIRE(!basic.VerifySecure(pks, agg, Bytes(msg))); +} + TEST_CASE("CheckValid for Legacy") { SECTION("Invalid G1 points should throw in CheckValid but not in FromBytes") @@ -1975,6 +2221,114 @@ TEST_CASE("CheckValid for Legacy") } } +// Locates test-vectors/reference.txt: an environment variable overrides +// the path the build system compiled in; source-tree-relative paths cover +// running the binary by hand. +static std::string ReferenceVectorsPath() +{ + std::vector candidates; + if (const char* env = std::getenv("DASHBLS_REFERENCE_VECTORS")) { + candidates.push_back(env); + } +#ifdef DASHBLS_REFERENCE_VECTORS + candidates.push_back(DASHBLS_REFERENCE_VECTORS); +#endif + candidates.push_back("test-vectors/reference.txt"); + candidates.push_back("../test-vectors/reference.txt"); + candidates.push_back("../../test-vectors/reference.txt"); + for (const auto& c : candidates) { + if (std::ifstream(c).good()) return c; + } + std::string tried; + for (const auto& c : candidates) tried += "\n " + c; + FAIL("reference vectors not found; tried:" << tried); + return ""; +} + +TEST_CASE("Reference vectors") +{ + // Every consensus-visible behavior of the library, compared line by line + // against the output of the relic-based implementation. + const std::string path = ReferenceVectorsPath(); + INFO("reference file: " << path); + + std::vector expected; + std::ifstream in(path); + for (std::string line; std::getline(in, line);) { + if (line.empty() || line[0] == '#') continue; + expected.push_back(line); + } + REQUIRE(expected.size() > 300); + + std::vector actual; + for (const auto& line : bls_test_vectors::GenerateReferenceVectors()) { + if (line.empty() || line[0] == '#') continue; + actual.push_back(line); + } + + REQUIRE(actual.size() == expected.size()); + for (size_t i = 0; i < expected.size(); i++) { + INFO("vector " << i + 1 << " of " << expected.size()); + REQUIRE(actual[i] == expected[i]); + } +} + +TEST_CASE("GTElement") +{ + SECTION("GTElement serialization") + { + vector seed(32, 0x05); + blst_p1_affine p1_affine; + + PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); + G1Element pk1 = BasicSchemeMPL().SkToG1(sk1); + pk1.CheckValid(); + pk1.ToAffine(&p1_affine); + GTElement gt1 = GTElement::FromAffine(p1_affine); + + auto outbuf = gt1.Serialize(); + + auto gt2 = GTElement::FromBytesUnchecked(outbuf); + + REQUIRE(gt1 == gt2); + } + + SECTION("GTElement pairing") + { + vector seed(32, 0x05), seed2(32, 0x06); + vector msg1 = {7, 8, 9}; + vector msg2 = {10, 11, 12}; + + auto sk1 = BasicSchemeMPL().KeyGen(seed); + auto sk2 = BasicSchemeMPL().KeyGen(seed2); + + auto pk1 = sk1.GetG1Element(); + auto pk2 = sk2.GetG1Element(); + + auto sig1 = BasicSchemeMPL().Sign(sk1, msg1); + auto sig2 = BasicSchemeMPL().Sign(sk2, msg2); + + auto aggsig = BasicSchemeMPL().Aggregate({sig1, sig2}); + + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{msg1, msg2}, aggsig)); + + auto pair1 = pk1.Pair(G2Element::FromMessage( + msg1, + (const uint8_t*)BasicSchemeMPL::CIPHERSUITE_ID.c_str(), + BasicSchemeMPL::CIPHERSUITE_ID.length())); + auto pair2 = pk2.Pair(G2Element::FromMessage( + msg2, + (const uint8_t*)BasicSchemeMPL::CIPHERSUITE_ID.c_str(), + BasicSchemeMPL::CIPHERSUITE_ID.length())); + auto pair = pair1 * pair2; + + auto agg_sig_pair = G1Element::Generator().Pair(aggsig); + + REQUIRE(pair == agg_sig_pair); + } +} + int main(int argc, char* argv[]) { int result = Catch::Session().run(argc, argv); diff --git a/src/threshold.cpp b/src/threshold.cpp index 4561dccdc..8526832b7 100644 --- a/src/threshold.cpp +++ b/src/threshold.cpp @@ -7,75 +7,9 @@ #include "threshold.hpp" #include "schemes.hpp" -#include "bn_helpers.hpp" static std::unique_ptr pThresholdScheme(new bls::LegacySchemeMPL); -/** - * Inverts a prime field element using the Euclidean Extended Algorithm, - * using bns and a custom prime modulus. - * - * @param[out] c - the result. - * @param[in] a - the prime field element to invert. - * @param[in] p - the custom prime modulus. - */ -static void fp_inv_exgcd_bn(bn_t c, const bn_t u_in, const bn_t p) { - bn_t u, v, g1, g2, q, r; - - bn_null(u); - bn_null(v); - bn_null(g1); - bn_null(g2); - bn_null(q); - bn_null(r); - - RLC_TRY { - bn_new(u); - bn_new(v); - bn_new(g1); - bn_new(g2); - bn_new(q); - bn_new(r); - - /* u = a, v = p, g1 = 1, g2 = 0. */ - bn_copy(u, u_in); - bn_copy(v, p); - bn_set_dig(g1, 1); - bn_zero(g2); - - /* While (u != 1. */ - while (bn_cmp_dig(u, 1) != RLC_EQ) { - /* q = [v/u], r = v mod u. */ - bn_div_rem(q, r, v, u); - /* v = u, u = r. */ - bn_copy(v, u); - bn_copy(u, r); - /* r = g2 - q * g1. */ - bn_mul(r, q, g1); - bn_sub(r, g2, r); - /* g2 = g1, g1 = r. */ - bn_copy(g2, g1); - bn_copy(g1, r); - } - - if (bn_sign(g1) == RLC_NEG) { - bn_add(g1, g1, p); - } - bn_copy(c, g1); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - bn_free(u); - bn_free(v); - bn_free(g1); - bn_free(g2); - bn_free(q); - bn_free(r); - }; -} - namespace bls { namespace Poly { @@ -90,43 +24,32 @@ namespace bls { } // end namespace Poly struct PolyOpsBase { - bn_t order; - bn_t iv; - - PolyOpsBase() { - bn_new(order); - gt_get_ord(order); - bn_new(iv); + static bool IsZero(const blst_scalar& a) { + std::array zero{}; + return memcmp(&a, &zero, sizeof(blst_scalar)) == 0; } - ~PolyOpsBase() { - bn_free(iv); - bn_free(order); + void MulFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_mul_n_check(&r, &a, &b); } - void MulFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_mul(r, a, b); - ModOrder(r); + void AddFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_add_n_check(&r, &a, &b); } - void AddFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_add(r, a, b); - ModOrder(r); + void SubFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_sub_n_check(&r, &a, &b); } - void SubFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_sub(r, a, b); - ModOrder(r); + void DivFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_scalar iv; + blst_sk_inverse(&iv, &b); + blst_sk_mul_n_check(&r, &a, &iv); } - void DivFP(bn_t& r, const bn_t& a, const bn_t& b) { - fp_inv_exgcd_bn(iv, b, order); - bn_mul(r, a, iv); - ModOrder(r); - } - - void ModOrder(bn_t& r) { - bn_mod(r, r, order); + void ScalarFromBytes(blst_scalar& r, const uint8_t* bytes) { + // reduces mod the group order, like bn_read_bin + bn_mod + blst_scalar_from_be_bytes(&r, bytes, Poly::nIdSize); } }; @@ -139,7 +62,7 @@ namespace bls { return PrivateKey::Aggregate({a, b}); } - PrivateKey Mul(const PrivateKey& a, const bn_t& b) { + PrivateKey Mul(const PrivateKey& a, const blst_scalar& b) { return a * b; } }; @@ -150,7 +73,7 @@ namespace bls { return a + b; } - G1Element Mul(const G1Element& a, const bn_t& b) { + G1Element Mul(const G1Element& a, const blst_scalar& b) { return a * b; } }; @@ -161,7 +84,7 @@ namespace bls { return a + b; } - G2Element Mul(const G2Element& a, bn_t& b) { + G2Element Mul(const G2Element& a, const blst_scalar& b) { return a * b; } }; @@ -174,13 +97,12 @@ namespace bls { throw std::length_error("At least 2 coefficients required"); } - BnGuard x; - bn_read_bin(x.val, id.begin(), Poly::nIdSize); - ops.ModOrder(x.val); + blst_scalar x; + ops.ScalarFromBytes(x, id.begin()); BLSType y = vecIn.back(); for (int i = (int) vecIn.size() - 2; i >= 0; i--) { - y = ops.Mul(y, x.val); + y = ops.Mul(y, x); y = ops.Add(y, vecIn[i]); } @@ -205,35 +127,34 @@ namespace bls { */ const size_t k = vec.size(); - BnArrayGuard delta(k); - BnArrayGuard ids2(k); + std::vector delta(k); + std::vector ids2(k); for (size_t i = 0; i < k; i++) { - bn_read_bin(ids2[i], ids[i].begin(), Poly::nIdSize); - ops.ModOrder(ids2[i]); + ops.ScalarFromBytes(ids2[i], ids[i].begin()); } - BnGuard a, b, v; + blst_scalar a, b, v; - bn_copy(a.val, ids2[0]); + a = ids2[0]; for (size_t i = 1; i < k; i++) { - ops.MulFP(a.val, a.val, ids2[i]); + ops.MulFP(a, a, ids2[i]); } - if (bn_is_zero(a.val)) { + if (PolyOpsBase::IsZero(a)) { throw std::invalid_argument("Zero id"); } for (size_t i = 0; i < k; i++) { - bn_copy(b.val, ids2[i]); + b = ids2[i]; for (size_t j = 0; j < k; j++) { if (j != i) { - ops.SubFP(v.val, ids2[j], ids2[i]); - if (bn_is_zero(v.val)) { + ops.SubFP(v, ids2[j], ids2[i]); + if (PolyOpsBase::IsZero(v)) { throw std::invalid_argument("Duplicate id"); } - ops.MulFP(b.val, b.val, v.val); + ops.MulFP(b, b, v); } } - ops.DivFP(delta[i], a.val, b.val); + ops.DivFP(delta[i], a, b); } /* @@ -270,7 +191,7 @@ namespace bls { G2Element Threshold::SignatureRecover(const std::vector& sigs, const std::vector& ids) { return Poly::LagrangeInterpolate(sigs, ids); } - + G2Element Threshold::Sign(const PrivateKey& privateKey, const Bytes& vecMessage) { return pThresholdScheme->Sign(privateKey, vecMessage); } diff --git a/test-vectors/reference.txt b/test-vectors/reference.txt new file mode 100644 index 000000000..3494cfe38 --- /dev/null +++ b/test-vectors/reference.txt @@ -0,0 +1,324 @@ +# dashbls reference vectors v1 +A.sk.0=542b7d7900f6335cba45d5d7a53d59d73f26f230905bfcd6ad91ebb85eaa008a +A.pk.basic.0=9331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.pk.legacy.0=1331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.fp.basic.0=d4ddc775 +A.fp.legacy.0=ca704224 +A.skG2.basic.0=9131d5ad87990f02258898a22680b72d1762a084fd23f793b94477473deb23dbaf5e3a0635aa8bd373136c0bf84d4dbd03d21a5719c4b680b72be4649635262df36794ceeea24865dd55705e4370ef96346efe7795b37f27eb8ae49f134fcad0 +A.skG2.legacy.0=03d21a5719c4b680b72be4649635262df36794ceeea24865dd55705e4370ef96346efe7795b37f27eb8ae49f134fcad01131d5ad87990f02258898a22680b72d1762a084fd23f793b94477473deb23dbaf5e3a0635aa8bd373136c0bf84d4dbd +A.pk.rt.legacy.0=1331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.pk.rt.basic.0=9331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +B.lsig.legacy.0=8a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8091006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea +B.lsig.basic.0=a91006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea0a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8 +B.lverify.0=1 +B.lverify.bad.0=0 +B.lsig.rt.0=8a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8091006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea +B.bsig.basic.0=8d3631d71a4f57c50f8108adb0cada2be41fba7bb1eced6e2b1757204f4103bbe41b0205cddd12a0c1f723633751eef517dc82561728b2b3482e74a4bbac58ac8851dad5247d11ad9b549690805fd078e02f39a6688e7db82f3a077c2f7dd4ab +B.bsig.legacy.0=17dc82561728b2b3482e74a4bbac58ac8851dad5247d11ad9b549690805fd078e02f39a6688e7db82f3a077c2f7dd4ab0d3631d71a4f57c50f8108adb0cada2be41fba7bb1eced6e2b1757204f4103bbe41b0205cddd12a0c1f723633751eef5 +B.bverify.0=1 +A.child.hard.0=13673dc18365d4d8d67005749ed2fba3c92dac9a259f3dac769288177d2de1ac +A.child.soft.0=43f1ad49de1fac8a337f01f27c48720cf0e453cb48cd8112c4c6aecf191c393b +A.sk.1=0d80059486148cf5dd43d6c2b42d41378e0e2cc354e328089fd5cc1eafb20549 +A.pk.basic.1=b4c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.pk.legacy.1=94c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.fp.basic.1=68f69bbe +A.fp.legacy.1=00eeb456 +A.skG2.basic.1=91005feee5588ac7887cbd365aff07a2e255cddfaff09016669f7993d68cd9a63793dcade435c0d12e875a683b3cd69a170cff1dd1e7a4e7ce4f7a3538ee76cef7b555524c2e302e4eb8ac2c759b1fa0631395df06e6eae95ca0c3aa82731576 +A.skG2.legacy.1=170cff1dd1e7a4e7ce4f7a3538ee76cef7b555524c2e302e4eb8ac2c759b1fa0631395df06e6eae95ca0c3aa8273157611005feee5588ac7887cbd365aff07a2e255cddfaff09016669f7993d68cd9a63793dcade435c0d12e875a683b3cd69a +A.pk.rt.legacy.1=94c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.pk.rt.basic.1=b4c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +B.lsig.legacy.1=103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea043ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457 +B.lsig.basic.1=843ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea +B.lverify.1=1 +B.lverify.bad.1=0 +B.lsig.rt.1=103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea043ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457 +B.bsig.basic.1=80f49f1b340f745c84ab7b51bbb742a6f82fbc04204398683dbcc861ed1f87fa45180c6ba9d5950347fc8d14cae6bc8f105a6d4978bf6c4333b489c656ad1eb3668f7584430a8f9c24349e6199e791e64627bde51a6af2f2905b58c12b121fb6 +B.bsig.legacy.1=105a6d4978bf6c4333b489c656ad1eb3668f7584430a8f9c24349e6199e791e64627bde51a6af2f2905b58c12b121fb600f49f1b340f745c84ab7b51bbb742a6f82fbc04204398683dbcc861ed1f87fa45180c6ba9d5950347fc8d14cae6bc8f +B.bverify.1=1 +A.child.hard.1=35bf9c36f494622db64c5173493cff599df5e765d687742cb6533b1d8c6d886d +A.child.soft.1=39827c4c7749881f819a8e5c1f4d9669d7c25c18166a71d15b633c2f89daf7e8 +A.sk.2=604f88a4ee4198b3668dc38ae604dedeb45464df19e941b57d5957a5372085d1 +A.pk.basic.2=b11bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.pk.legacy.2=911bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.fp.basic.2=919d796a +A.fp.legacy.2=0ef6fc3e +A.skG2.basic.2=835092be4610e6a7e5e131f10f58a53444c0f89fbc37250bf19157e28f0a34f211cfea8022ade9a4b8b53ae090129cf503245c6bb1356ca13f772291319fc284c1a76b396fa469bd6f32d3eccdc549ba6f95e4ca08fb3777087e006cf2caa524 +A.skG2.legacy.2=03245c6bb1356ca13f772291319fc284c1a76b396fa469bd6f32d3eccdc549ba6f95e4ca08fb3777087e006cf2caa524035092be4610e6a7e5e131f10f58a53444c0f89fbc37250bf19157e28f0a34f211cfea8022ade9a4b8b53ae090129cf5 +A.pk.rt.legacy.2=911bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.pk.rt.basic.2=b11bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +B.lsig.legacy.2=0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f48351510726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b +B.lsig.basic.2=8726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f4835151 +B.lverify.2=1 +B.lverify.bad.2=0 +B.lsig.rt.2=0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f48351510726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b +B.bsig.basic.2=b5a4aaf5174fc68468b7bab3b182ea6e55fb55843d17b1950383f934710cdaa6a77c4494e79ec674dfc3167bfc1d944216d3e13e435a0509da3e670ebc5aeef151f32558891e00b9e8560e47601662a8e4ea893ab20cc5e909640e33bbf41fd3 +B.bsig.legacy.2=96d3e13e435a0509da3e670ebc5aeef151f32558891e00b9e8560e47601662a8e4ea893ab20cc5e909640e33bbf41fd315a4aaf5174fc68468b7bab3b182ea6e55fb55843d17b1950383f934710cdaa6a77c4494e79ec674dfc3167bfc1d9442 +B.bverify.2=1 +A.child.hard.2=140053409896043b8b7434dc29f14d132b3cbbd139677ff397a1c10f8f364616 +A.child.soft.2=525eb8eba7332727e1b0ff22bdf6320eb401d3d5e1caebdd72b0fa8aa2f0e942 +A.sk.3=21160cba355200250e9fad6bca96ed116930dc632dce23081636fedeb106c83b +A.pk.basic.3=982f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.pk.legacy.3=182f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.fp.basic.3=b7fea396 +A.fp.legacy.3=f71bdf44 +A.skG2.basic.3=a9a60b869403189f7abc1abf476d8afc27e4a8502f9a4949227f03393b793050a5a16c9b89d084452ff79741fc684925188c8776bc0b799fff488f0214a3b875d543b626c18a9ac0c8b6447de08c82aa735728727458305f69f77882b9d2098a +A.skG2.legacy.3=988c8776bc0b799fff488f0214a3b875d543b626c18a9ac0c8b6447de08c82aa735728727458305f69f77882b9d2098a09a60b869403189f7abc1abf476d8afc27e4a8502f9a4949227f03393b793050a5a16c9b89d084452ff79741fc684925 +A.pk.rt.legacy.3=182f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.pk.rt.basic.3=982f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +B.lsig.legacy.3=0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce57207acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa +B.lsig.basic.3=87acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce572 +B.lverify.3=1 +B.lverify.bad.3=0 +B.lsig.rt.3=0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce57207acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa +B.bsig.basic.3=8300e9d700c1d263e5d9caec9a3302aa8984b515bd961f25b913de852168ecc0e0dcea0913a7359bee57071208895ea812bd1ae92601ba7f2e8d6d6809330ef06dde1c8ff633cff5db38a63b5aec867d43a6e3e9bafac7aec876d6625666a5cd +B.bsig.legacy.3=12bd1ae92601ba7f2e8d6d6809330ef06dde1c8ff633cff5db38a63b5aec867d43a6e3e9bafac7aec876d6625666a5cd0300e9d700c1d263e5d9caec9a3302aa8984b515bd961f25b913de852168ecc0e0dcea0913a7359bee57071208895ea8 +B.bverify.3=1 +A.child.hard.3=58a543b659628491544fc0f85d4938820dc19d5591d53f49c8b9d950d433aff7 +A.child.soft.3=0c9bd3c62871a9e36c947f7a7ab764a69b1fdb41f9625b30bd3a5bd91484f5b6 +A.sk.4=0b23c79ec3331dd7a2d4a8708b21263330b9a9d8891a81360744a2a483ea884a +A.pk.basic.4=8564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.pk.legacy.4=0564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.fp.basic.4=228139d6 +A.fp.legacy.4=8710d0bc +A.skG2.basic.4=833682df2e6ae024e5d9bfb5c5dc15cb855d55e945693979abfb2078aed37421438782b7d6b17345bdac09f86563327b04f35a0374e59bc64ef3ee948dc4cd4689c4f55f298c6a5ad9703e823f853d07c958b87aa151a066d31a95cfec1da928 +A.skG2.legacy.4=04f35a0374e59bc64ef3ee948dc4cd4689c4f55f298c6a5ad9703e823f853d07c958b87aa151a066d31a95cfec1da928033682df2e6ae024e5d9bfb5c5dc15cb855d55e945693979abfb2078aed37421438782b7d6b17345bdac09f86563327b +A.pk.rt.legacy.4=0564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.pk.rt.basic.4=8564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +B.lsig.legacy.4=11fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20198fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f1 +B.lsig.basic.4=998fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f111fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20 +B.lverify.4=1 +B.lverify.bad.4=0 +B.lsig.rt.4=11fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20198fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f1 +B.bsig.basic.4=a57f7ec0d09676c12b4fb525a1d359b7c29cc0f010682905f0a44bafc8764998c85fb885f9affeb2e256cfb3f5558c61180da06bedd87410c1f842ddcf467b04aeed09b4cb8c2eb4dbf5f502cea4683e6c35cf2475208fa3dcd852e5d290faf2 +B.bsig.legacy.4=980da06bedd87410c1f842ddcf467b04aeed09b4cb8c2eb4dbf5f502cea4683e6c35cf2475208fa3dcd852e5d290faf2057f7ec0d09676c12b4fb525a1d359b7c29cc0f010682905f0a44bafc8764998c85fb885f9affeb2e256cfb3f5558c61 +B.bverify.4=1 +A.child.hard.4=1422752ec6e3f867f5f80020bec1d84e6ade884bf4093f4d337f503417e00fdd +A.child.soft.4=10b37a4b07fa1a78786efbe3f116dfd462ba0ce94e7afa742f5a87234c3a868f +A.sk.5=0535569670f3c8c0379aea8c93b3bedf5ef16672417c281da18b957f41398d13 +A.pk.basic.5=b14d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.pk.legacy.5=914d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.fp.basic.5=1c9dbfe5 +A.fp.legacy.5=708cb0c8 +A.skG2.basic.5=8af786a3d660b1305eee25239d7c8f225fa604b23f96287745b9caee6a039ff21733bbe644df10daaf79c0ddc187bf4811d3f20781f42de6cb8fae96a560ed9f42aff7d3fee427f7bb44967dbbaff01e3efdad10b83a33076246b5819d1ef755 +A.skG2.legacy.5=11d3f20781f42de6cb8fae96a560ed9f42aff7d3fee427f7bb44967dbbaff01e3efdad10b83a33076246b5819d1ef7550af786a3d660b1305eee25239d7c8f225fa604b23f96287745b9caee6a039ff21733bbe644df10daaf79c0ddc187bf48 +A.pk.rt.legacy.5=914d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.pk.rt.basic.5=b14d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +B.lsig.legacy.5=116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc07c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5 +B.lsig.basic.5=87c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc +B.lverify.5=1 +B.lverify.bad.5=0 +B.lsig.rt.5=116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc07c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5 +B.bsig.basic.5=98e09cacf9f860bc131c401c6dc20a96cba00c581a2abf576a58ae4e0edd96d82764f95e1b0a00d6e6ad52db83adb92b16349f9b0675e0b6ffe90ec64203fce107813ae36cfb3a4c98b40580b362d22b27595d57e1f02ab67534b96b186ee2fe +B.bsig.legacy.5=16349f9b0675e0b6ffe90ec64203fce107813ae36cfb3a4c98b40580b362d22b27595d57e1f02ab67534b96b186ee2fe18e09cacf9f860bc131c401c6dc20a96cba00c581a2abf576a58ae4e0edd96d82764f95e1b0a00d6e6ad52db83adb92b +B.bverify.5=1 +A.child.hard.5=186953b6ebeb46b7d6d17f4eb6b55738a8910468d8c7a12773fa4ece4d396e33 +A.child.soft.5=36aae8999b8d557746cb7310a2291c8facd190430c6f769f60193610a5e7f570 +A.sk.6=3e54ca602df6ef98b894e98a87eddca31b87fd052cfcab698477b3ad19c84dfb +A.pk.basic.6=925b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.pk.legacy.6=125b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.fp.basic.6=910eddce +A.fp.legacy.6=08462c2f +A.skG2.basic.6=ab3a64185b1e43943f8645682b86e13882653beeee52343ced9b87556c6eb39f2b87cd0fffe68276470a4c4afc21445413753b7fee41fa154a0b76495935a97243279fd70a0baff73cd8ae88c2b387ccd358cac5d7c34af4cc7e348c0fea602d +A.skG2.legacy.6=93753b7fee41fa154a0b76495935a97243279fd70a0baff73cd8ae88c2b387ccd358cac5d7c34af4cc7e348c0fea602d0b3a64185b1e43943f8645682b86e13882653beeee52343ced9b87556c6eb39f2b87cd0fffe68276470a4c4afc214454 +A.pk.rt.legacy.6=125b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.pk.rt.basic.6=925b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +B.lsig.legacy.6=825a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac09aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d +B.lsig.basic.6=a9aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d025a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac +B.lverify.6=1 +B.lverify.bad.6=0 +B.lsig.rt.6=825a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac09aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d +B.bsig.basic.6=b8801cf2113e290828ae4c2ef629d8cb3d3a34d3b8de7dcd0170f6a070f0787941ca2ed97d561c1b069b688945efb525033f27242eace77e730ce5c0a1200b05be002b47b2a7664445537f0ad10dbf24bbf0cc0f60ccd6992df41f3030f71c2a +B.bsig.legacy.6=833f27242eace77e730ce5c0a1200b05be002b47b2a7664445537f0ad10dbf24bbf0cc0f60ccd6992df41f3030f71c2a18801cf2113e290828ae4c2ef629d8cb3d3a34d3b8de7dcd0170f6a070f0787941ca2ed97d561c1b069b688945efb525 +B.bverify.6=1 +A.child.hard.6=5cec6bb344ecd98a133343f77b84fe620d46b1468a4579c83963cc0d32be15fc +A.child.soft.6=49f50d890fa3b2d475a6ce1bb2c56f9508fe8d2662659146e3434390ddc0e94d +A.sk.7=0e3c506a5cc9be55237397674e0fe32f61e4ecb6e2c761493d89c7a17a6dcc0d +A.pk.basic.7=9661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.pk.legacy.7=1661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.fp.basic.7=e141846a +A.fp.legacy.7=c4d6d99d +A.skG2.basic.7=86d69b68a97adf54d024ce7fef27dbc0b3f08b993daec1434b5b8f77b0f09614e0ff35c5e24b4e3b57c378e5370e2f3715847268902520804e69b0c74f7744ccefed304bbf4f06103874fba5468add684faf46d5719fdb2570e398cef37e64cb +A.skG2.legacy.7=15847268902520804e69b0c74f7744ccefed304bbf4f06103874fba5468add684faf46d5719fdb2570e398cef37e64cb06d69b68a97adf54d024ce7fef27dbc0b3f08b993daec1434b5b8f77b0f09614e0ff35c5e24b4e3b57c378e5370e2f37 +A.pk.rt.legacy.7=1661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.pk.rt.basic.7=9661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +B.lsig.legacy.7=946ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf70035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f +B.lsig.basic.7=a035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f146ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf7 +B.lverify.7=1 +B.lverify.bad.7=0 +B.lsig.rt.7=946ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf70035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f +B.bsig.basic.7=ae7378473f2e89255e78676f4ee798ea8e169551dfcc8b78cd564dbdf5d1495c0145e95c7e5d0df191c955a40ba033b805363357f176bd8b84063ae47bf7e3a273a2644ba437a9c496a66761a0693305b3249f588dd1e45feef9d33e1030c076 +B.bsig.legacy.7=85363357f176bd8b84063ae47bf7e3a273a2644ba437a9c496a66761a0693305b3249f588dd1e45feef9d33e1030c0760e7378473f2e89255e78676f4ee798ea8e169551dfcc8b78cd564dbdf5d1495c0145e95c7e5d0df191c955a40ba033b8 +B.bverify.7=1 +A.child.hard.7=6be06280ee13d42a1d9f12b5d3e9b429641ede55b3bad11a27948123e89d8913 +A.child.soft.7=1605a13324f75c0755fe03bfb1a9e9478586c12a54332de98ec4658dc233ce14 +C.aggsec.legacy.1=17b20ce50a20f09a4d807d4c0db622d13be8902380e908a9304d21d03520cb9712e8fb478bd4eb35477298c05ad5069610688edde3431521fa64f24c6e6fad0b38fd77ae2edd71871948273c6a17efad03b2f908fa9717935fc7ae68ca7421ec +C.versec.legacy.1=1 +C.aggsec.basic.1=98103b71225c000a8e08507ac38248bc97ff96c4ef0a0ecb610ba97798df58f741421304b46dbe709d891640bdc797d316e5b1fe117273391a37e981838677ce2185afe88e4f29a51ceb6d7c9a7f22062e8d4029345f2f37a473b2a8055ed848 +C.versec.basic.1=1 +C.versec.legacy.bad.1=0 +C.agg.G2.1=a941eab017de217c825e0724f3ec1bfbfd90ce7628f5d3bbf0806e0efdfa806fa746c178da66fb2a8d9a587ef0bab4c91774469f443f513972a0c59ae55278c32e1caf9c66d52e708e53ef2828c48f5c4b830d256c2ff1b155730588dfc9adeb +C.agg.G1.1=9503c392ceae7db1e2f247fb6148b4211708a74491989916daeec3c34bdd2803a98fc3a43976daaab00903211b1d1c46 +C.agg.sk.1=51542fee7895e4987de3b93ff52af9bb129dc076f0dcb8afe5d93460f2a6c1b7 +C.aggsec.legacy.2=0ca923fd169db16838c09804d7150f2bdde930d17040bc39c553baf9336527b15cbe83aa841b9f902a203521e7bc60220a9c9e1ddf0677564be73f7b8f9ce2a52c28c910eac2baba3ef3310fad7eaa8baf9465bfc9104f0ac08cd9da8b41b70e +C.versec.legacy.2=1 +C.aggsec.basic.2=a165590855562a1437e0d5fce7dfb1373a07fe09bdb33cf5f82b2e8d3b809ce7a8e9ae47eb0602181d474866958d51641816952a467643164dab9aa92567dac5449943f8464d0760740fcbb54bd758cc80e232032f56f3df269460d2cd7b807c +C.versec.basic.2=1 +C.versec.legacy.bad.2=0 +C.agg.G2.2=ab45f84e0c325239430f0584bf220bbabcff667d308d39c1fcd827f8c7f105e8210c20ca619ad9084984914e35fc0d1f082b5675dcfd07041f6ee1564de37b678a4b68ee51db8977bdc79f4ba1c9d49cccef35b1de0b7e8494c9cc5fa233edd0 +C.agg.G1.2=a5425f1689e33cd676a112efdd5aa80df7b137fad1db3c690bb2213745dd9a25c51d35fce39de1eaf5e268ccfdcb9163 +C.agg.sk.2=4e1643205fe0ed6f883f3d803c4742a2db0b7826aa523e5bc62a18c766d698df +C.aggsec.legacy.3=87b142eb28bb97ed9839900dbcefe90a992b4cd63468dd13d3dc8bcef394f0e77715f8da3fd62d3847ab845b2035a880193d116414b1a51a43edce8b2ccc4ee08f4492d35e19f9c35fe76cf73598afc085e62af9a6e52927c8f4d8544f9887f4 +C.versec.legacy.3=1 +C.aggsec.basic.3=8959bdf6c216c1c50eee30aa5ccd21196c058e3e28069cc70d86f4dc1608bbf9b791db866fb23ee816acd9b8ed3d3964174a16f05c9afc9cdc51790e8bf7a4ea0b4aef4645119a041ff97fa8500d6167c0db4743aba27f454853451f7229a287 +C.versec.basic.3=1 +C.versec.legacy.bad.3=0 +C.agg.G2.3=85b735e626a9322824cdb16d704db491330aa37239153a833c212f5404a1028f97d8339d437d7cdf06749330639c6ea21437d0bc3b3e6e1d411ed606e1943576c462abf6d572c9eccabdaec263073748421f84497f775a93631ccb51364f528f +C.agg.G1.3=b45111e7f20ff82bbfce790f2abeefebb885844423ed1fd37b43904b7b47454ff3c30ac47d4969c615af7316b1f41ffb +C.agg.sk.3=1d3b4ed79d0c0001f1bb2ded98523121bedbeaa37ac4fceb0f0bfdf4d8963849 +C.aggsec.legacy.10=0eab90239c026690d3ba0ac74c69f28420931128800ff1cfa7c64668600b99e7f60853e0f2d22b4d6cf07d97428cd0411256b05ad14e7ecae1372673d5c00db1880874016ea8a2ed6eb34655297e7883e6e023f28a6aa206e0fd0294bff532fa +C.versec.legacy.10=1 +C.aggsec.basic.10=a0368e7182619f7569d087c364199db4c5c20ba3e090706a3767ceb5dc054afe66fb4835b487eb17f67f36972b5e2d660c207b224abf7b30bb7db3bd16ebe0d1a87fac41370d82656af3e907293e954a884411765caf76708008ed2dd9b25622 +C.versec.basic.10=1 +C.versec.legacy.bad.10=0 +C.agg.G2.10=82974b5dafcc908dd00b725f120e9fa3ba1ec265019145a898b6ab3ddebb6f0b1a8c02827e04ee33dca6f098799ba76710b0cb5bf3b125ccd295abf5f3eca6fa272be6d727a591b32208b33618212368a15035b7e97edd2e29112e91bf61b651 +C.agg.G1.10=86689658c56d1d945a480cc1a11cb77ec19bce8fd3f8f6f5dbb1db20330aac73e78268a66e512b5ddd98fffc20bc7bbc +C.agg.sk.10=4972fe548df20e3ede53ebe27f0ab0c1bfe742903357d88d48301c6b7c9a77b6 +D.lagg=8acadc114967f7a21953644c19dc9ecbfdbdb0233d484862e2816ef3e6cc94177c85d26a3d8bc10a79ba7fced80226530f01ee9e0f441c83c8b50bf5e03be1f1dbfcc7d660d6d968c738693574f144dc1c5001a221bdf16d7127cb828e58003b +D.laggverify=1 +D.laggverify.bad=0 +E.mastersig=99a667d463e60f2e2b3feecb2a04061692faa66c543bd4eb36c5077794b2bfe24200aa1709fa72408a40a8dfe8a00d8c0d6d35dbce43924eba4354c109b6370a722f67bed82b4d1d984572d7817797477cd34aa415d20c996d573d5ed6043574 +E.masterverify=1 +E.skshare.0=2fe9aa3dc24ee2b902c2632305e5e9f9b667cb721239b1eeaec32775235696ce +E.pkshare.0=93ac3b88ca6f2a31eb5929c590692f415b3e9cb4dabb70bf9493a6858e1fe86d89ed2363f2098d0888e3fc601947fda0 +E.sharematch.0=1 +E.sigshare.poly.0=8c2810b9171817b12be33be0d8caa159785f05c30695aacba1cfac14bd4b6c4a6879ebc91a471c1605f608e2e223ce62044251375a33a8c918177fc950496fbfd4832d22f8ba8c4b391b74113703e82828d60eb468be93e150eee50bf0a0927e +E.sigshare.sign.0=8c2810b9171817b12be33be0d8caa159785f05c30695aacba1cfac14bd4b6c4a6879ebc91a471c1605f608e2e223ce62044251375a33a8c918177fc950496fbfd4832d22f8ba8c4b391b74113703e82828d60eb468be93e150eee50bf0a0927e +E.skshare.1=6d7a99964fe9909c9a713f88392b32af635db3daec83b4538c6fc3228ecdf513 +E.pkshare.1=8e333381aedd9b63aaf2e019e197e6983ca3749b8561e21485bc6b7a4bef92d0ecd50481bf95832d87d3b2bd37d3a141 +E.sharematch.1=1 +E.sigshare.poly.1=860e104d01a3d7c3af84995bdd1574c4292ea7e5373d22ccd590ac33acaa91d2f8d119fb2f4980ca4fee9ec66179f9f10f053bd123ec34abdd0e181d76bbb795459f65c1b1404904aa6d76593561b67b2c3f29d0e965533161a43e1683e4d1ea +E.sigshare.sign.1=860e104d01a3d7c3af84995bdd1574c4292ea7e5373d22ccd590ac33acaa91d2f8d119fb2f4980ca4fee9ec66179f9f10f053bd123ec34abdd0e181d76bbb795459f65c1b1404904aa6d76593561b67b2c3f29d0e965533161a43e1683e4d1ea +E.skshare.2=4a691d73ddb6e5236c82bab66d69ba08c340e25aad705836792c66a8b8b2391f +E.pkshare.2=83ad1776d2dfec1bde34672dd518bd59c3861e92f374fd34e11bfc454a29d7c92536bd0e4c48288ab704211f5053c1c5 +E.sharematch.2=1 +E.sigshare.poly.2=12e5310660dfc34e159a5910db2b2b517936acde9eadd3c954c572c3f7814a07a0350ca68f56cc4277fd9784d78182bd04588688bd1c842992e941f30939f2bc4325237ebf4b30a490473878636f72e7cca9036895141d01ccef721da90fa3ff +E.sigshare.sign.2=12e5310660dfc34e159a5910db2b2b517936acde9eadd3c954c572c3f7814a07a0350ca68f56cc4277fd9784d78182bd04588688bd1c842992e941f30939f2bc4325237ebf4b30a490473878636f72e7cca9036895141d01ccef721da90fa3ff +E.skshare.3=296fdf7a3c8e8d9a1d80009e1e54c95ce84bc381e63236e500729d4a6a5f0d0f +E.pkshare.3=893bbd2c6b4d595cdac30778e438a1b95d16eb1909b8bcc28148848507d07e98793d4c0368d328d45a5355df1f2253da +E.sharematch.3=1 +E.sigshare.poly.3=039b5d75dfad13ce4392deaa7d207a12e0a658dd5f34f66098453e60b07b38fbc7b3271d14697e6bfd56d6053a697237069259997fd43f322cb917f7fed360cbacbe2a8582358c954c5381cdc442f8323394cab23d4ce0c2745ca6e45aa99c8b +E.sigshare.sign.3=039b5d75dfad13ce4392deaa7d207a12e0a658dd5f34f66098453e60b07b38fbc7b3271d14697e6bfd56d6053a697237069259997fd43f322cb917f7fed360cbacbe2a8582358c954c5381cdc442f8323394cab23d4ce0c2745ca6e45aa99c8b +E.skshare.4=57230fd28ec31c1a4c4a5e378c6fa748d6e68772fe83d35477bb6539876fab15 +E.pkshare.4=91edd2ed20fa5ef43bf796785e0ca8ab1d6cdb4ae63aba67ffccc0f6911c1214f81a62e23175f46b767681cf92cb25f1 +E.sharematch.4=1 +E.sigshare.poly.4=97c77544f1db5118e955e892c45d1895724b77e99efd420615f54f4d4d5b7b57f668926fa45be02190d269a2354cfaf30d55b722c035e19354e593015ccf0bd71d2c7e4c35e4cfb951bd6ff5f88008014f877a44f5f9d95d01fd1b1e570a3b15 +E.sigshare.sign.4=97c77544f1db5118e955e892c45d1895724b77e99efd420615f54f4d4d5b7b57f668926fa45be02190d269a2354cfaf30d55b722c035e19354e593015ccf0bd71d2c7e4c35e4cfb951bd6ff5f88008014f877a44f5f9d95d01fd1b1e570a3b15 +E.skrecover=6c04e5af86f2ddeaf6d1d4f8bbd4a1217b29abd4517d4619dada7ec087ed6f6f +E.skrecover.match=1 +E.pkrecover=8d713f7f50fe5d3e2c0335c3d3c0ceb2a1689eb3c2a4436c3a11898b8b797818aae1316825542790d1b0b2cafffb9150 +E.sigrecover=99a667d463e60f2e2b3feecb2a04061692faa66c543bd4eb36c5077794b2bfe24200aa1709fa72408a40a8dfe8a00d8c0d6d35dbce43924eba4354c109b6370a722f67bed82b4d1d984572d7817797477cd34aa415d20c996d573d5ed6043574 +E.sigrecover.verify=1 +E.sigrecover.same=1 +F.esk=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c2794953757b1115e7110046606404b38e4654adc4c2a52a5337deca0794080d3f92921 +F.esk.pk.legacy=89efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.esk.pk.basic=a9efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.esk.chaincode=4c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c279495 +F.child.legacy=00000001023e497ad38000002aff185013e7118856e4bcb6e2dc9608cab5219747c8037a889789b1be76feb6df1250aa4f4cb5f7e58f7b5f942eb2f2b99b36eacb3e6e65c4766ce956e628e62a +F.child.legacy.sk=1250aa4f4cb5f7e58f7b5f942eb2f2b99b36eacb3e6e65c4766ce956e628e62a +F.child.basic=000000010203f3628b8000002a447c89f7c80822b8661513be9c7fdf172a91faf71300f68fa39cfdea0ae3740f625c4936e02f4abcee9cfbd59e403428b9937f94bd0908cf676c3f63bc7713e0 +F.child.basic.sk=625c4936e02f4abcee9cfbd59e403428b9937f94bd0908cf676c3f63bc7713e0 +F.epk.legacy=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c27949589efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.epk.basic=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c279495a9efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.epk.child.legacy=00000001010b9a9e8000000001d84919fa70508fefd47576f19b437b3bb2049039740d83de455742dae48c96ab8a9f848f28b295903845d5f860c9031899e4f1a6d092edacdaec28f64a7fd9b94679b3faaf0ecf6b09f70b1d3ed5555f +F.epk.child.basic=00000001010b9a9e80000000012428f63e5f4b2e25871336f34ecebbd6e0e648d8b4a02ec033b575a391dc6b598cf85b6e8d78a797985402debeb2a1b823ab7c98ae20ae1609aaadd70309a4d40ef5cad6a6f93cb9af0ec2045dba96cf +F.pubpriv.match.legacy=1 +F.esk.rt=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c2794953757b1115e7110046606404b38e4654adc4c2a52a5337deca0794080d3f92921 +F.epk.rt.legacy=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c27949589efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +G.inrange.mod0=57c1d1f8ef9ad067fef7d0056bdfec87f51c28b65dd56fa3cbcc0f1afc984725 +G.inrange.mod1=57c1d1f8ef9ad067fef7d0056bdfec87f51c28b65dd56fa3cbcc0f1afc984725 +G.big.mod1=1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffd +G.big.mod0=EXC +G.order.mod1=0000000000000000000000000000000000000000000000000000000000000000 +G.order.mod0=73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +G.orderm1.mod0=73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000 +G.zero.sk=0000000000000000000000000000000000000000000000000000000000000000 +G.zero.pk=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +G.bip32seed=193afdb206c666de5cfba5d5a235477d269b1634ec36cfa1a3167cb47727bf25 +G.dh=a11d215631668626a3396afbfd38d850a2c18ffa90bd589714e40112b2592064c581eb8205fdc7bcd860e23b15e487cf +G.dh.sym=1 +H.g1.inf.basic.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.inf.basic.valid=1 +H.g1.inf.legacy.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.inf.legacy.valid=1 +H.g2.inf.basic.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.inf.basic.valid=1 +H.g2.inf.legacy=EXC +H.g1.badinf.basic=EXC +H.g1.badinf.legacy=EXC +H.g2.badinf.basic=EXC +H.g2.badinf.legacy=EXC +H.g1.zeros.basic=EXC +H.g1.zeros.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.zeros.legacy.valid=0 +H.g2.zeros.basic=EXC +H.g2.zeros.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.zeros.legacy.valid=0 +H.g1.zeros.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.zeros.legacy.unchk.valid=0 +H.g2.zeros.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.zeros.legacy.unchk.valid=0 +H.g1.ff.basic=EXC +H.g1.ff.legacy=EXC +H.g2.ff.basic=EXC +H.g2.ff.legacy=EXC +H.g1.fflow.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.fflow.legacy.valid=0 +H.g1.legacyAsBasic=EXC +H.g1.basicAsLegacy.ser=98138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.basicAsLegacy.valid=1 +H.g2.legacyAsBasic=EXC +H.g2.basicAsLegacy.ser=801627cb11cf0498af70b04e84ec07a615c01219b0693be59fb93e403f7937ddf26efe486c4b8edffdea8f136203b99e15240cfb56882628ef6d4b2cad852cbe23b882b2aab625f0e82f355ce1a4fe129b589d97ca3e17c5a36e514f93cfff32 +H.g2.basicAsLegacy.valid=0 +H.g1.negated.legacy.ser=98138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.negated.legacy.valid=1 +H.g1.negated.basic.ser=b8138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.negated.basic.valid=1 +H.g1.nonsub.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.legacy.valid=0 +H.g1.nonsub.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.legacy.unchk.valid=0 +H.g1.nonsub.basic=EXC +H.g1.nonsub.basic.unchk.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.basic.unchk.valid=0 +H.g2.nonsub.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.nonsub.legacy.valid=0 +H.g2.nonsub.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.nonsub.legacy.unchk.valid=0 +H.g2.nonsub.basic=EXC +H.g2.nonsub.basic.unchk=EXC +H.g1.nocurve.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +H.g1.nocurve.legacy.valid=0 +H.g1.nocurve.basic=EXC +H.g2.flag48.basic=EXC +H.len47=EXC +H.g1.nocurve.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +H.g1.nocurve.sign.legacy.valid=0 +H.g1.overp.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.overp.sign.legacy.valid=0 +H.g2.x2.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x2.sign.legacy.valid=0 +H.g2.x3.sign.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x3.sign.legacy.valid=0 +H.g2.x2.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x2.legacy.valid=0 +H.g2.overp.sign.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.overp.sign.legacy.valid=0 +I.basic.verify.infinf=0 +I.legacy.verify.infinf=0 +I.basic.verify.infpk=0 +I.basic.verify.infsig=0 +I.aggver.empty.infsig=1 +I.aggver.empty.realsig=0 +I.aggver.mismatch=0 +I.versec.withinf=0 +# end diff --git a/tools/fuzzgen.cpp b/tools/fuzzgen.cpp new file mode 100644 index 000000000..28a6bbebd --- /dev/null +++ b/tools/fuzzgen.cpp @@ -0,0 +1,165 @@ +// Differential fuzz driver for the relic -> blst migration. +// +// Uses only the public dashbls API; compile the same source against the old +// and new libraries, run with the same arguments, and diff the output. +// +// fuzzgen +// +// Covers: random byte-string decoding in every mode (the dominant +// consensus-facing attack surface), decode/re-encode round trips, and +// legacy + basic sign/verify/aggregate cycles on derived keys. + +#include +#include +#include +#include +#include +#include + +#include + +using namespace bls; +using std::vector; + +// xorshift128+ so both builds see identical byte streams with no libc +// dependency +static uint64_t s0, s1; +static uint64_t rng() +{ + uint64_t x = s0; + const uint64_t y = s1; + s0 = y; + x ^= x << 23; + s1 = x ^ y ^ (x >> 17) ^ (y >> 26); + return s1 + y; +} + +static void fill(vector& v) +{ + for (size_t i = 0; i < v.size(); i += 8) { + const uint64_t r = rng(); + for (size_t j = 0; j < 8 && i + j < v.size(); j++) + v[i + j] = (uint8_t)(r >> (8 * j)); + } +} + +static void dump(const vector& v) +{ + for (auto b : v) printf("%02x", b); +} + +static void probeG1(const vector& in, bool fLegacy) +{ + try { + G1Element e = G1Element::FromBytes(Bytes(in), fLegacy); + dump(e.Serialize(fLegacy)); + printf(" v%d", (int)e.IsValid()); + } catch (const std::exception&) { + printf("EXC"); + } + printf(";"); +} + +static void probeG2(const vector& in, bool fLegacy) +{ + try { + G2Element e = G2Element::FromBytes(Bytes(in), fLegacy); + dump(e.Serialize(fLegacy)); + printf(" v%d", (int)e.IsValid()); + } catch (const std::exception&) { + printf("EXC"); + } + printf(";"); +} + +int main(int argc, char** argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: fuzzgen \n"); + return 2; + } + s0 = strtoull(argv[1], nullptr, 0) * 0x9E3779B97F4A7C15ULL + 1; + s1 = s0 ^ 0xD1B54A32D192ED03ULL; + char* end = nullptr; + errno = 0; + const long iters = strtol(argv[2], &end, 0); + if (errno != 0 || end == argv[2] || *end != '\0' || iters <= 0) { + fprintf(stderr, "fuzzgen: the iteration count must be a positive number\n"); + return 2; + } + + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (long i = 0; i < iters; i++) { + printf("i%ld: ", i); + + // 1. random decodes: fully random, and "plausible" (top bits biased + // into the interesting flag space) + vector b48(48), b96(96); + fill(b48); + fill(b96); + switch (i % 4) { + case 0: break; + case 1: b48[0] &= 0x1f; b96[0] &= 0x1f; break; // no flags + case 2: b48[0] |= 0x80; b96[0] |= 0x80; break; // sign/compress + case 3: b48[0] = (uint8_t)(rng() & 0xe0); b96[0] = b48[0]; break; + } + probeG1(b48, false); + probeG1(b48, true); + probeG2(b96, false); + probeG2(b96, true); + + // 2. valid-point cross-mode round trips + vector seed(32); + fill(seed); + PrivateKey sk = basic.KeyGen(seed); + G1Element pk = sk.GetG1Element(); + probeG1(pk.Serialize(true), true); + probeG1(pk.Serialize(false), true); // modern bytes into legacy codec + + // 3. legacy + basic signing over a random 32-byte hash + vector h(32); + fill(h); + G2Element lsig = legacy.Sign(sk, Bytes(h)); + dump(lsig.Serialize(true)); + printf(" %d", (int)legacy.Verify(pk, Bytes(h), lsig)); + printf(";"); + G2Element bsig = basic.Sign(sk, Bytes(h)); + dump(bsig.Serialize(false)); + printf(" %d", (int)basic.Verify(pk, Bytes(h), bsig)); + printf(";"); + + // 4. secure aggregation with 3 keys + vector sks; + vector pks; + vector lsigs; + for (int k = 0; k < 3; k++) { + vector s2(32); + fill(s2); + sks.push_back(basic.KeyGen(s2)); + pks.push_back(sks.back().GetG1Element()); + lsigs.push_back(legacy.Sign(sks.back(), Bytes(h))); + } + G2Element agg = legacy.AggregateSecure(pks, lsigs, Bytes(h)); + dump(agg.Serialize(true)); + printf(" %d", (int)legacy.VerifySecure(pks, agg, Bytes(h))); + printf(";"); + + // 5. threshold 2-of-3 recovery + vector ids; + vector> idStore(3, vector(32)); + for (auto& id : idStore) fill(id); + PrivateKey sh0 = Threshold::PrivateKeyShare({sks[0], sks[1]}, Bytes(idStore[0])); + PrivateKey sh1 = Threshold::PrivateKeyShare({sks[0], sks[1]}, Bytes(idStore[1])); + try { + PrivateKey rec = Threshold::PrivateKeyRecover( + {sh0, sh1}, {Bytes(idStore[0]), Bytes(idStore[1])}); + dump(rec.Serialize()); + } catch (const std::exception&) { + printf("EXC"); + } + printf("\n"); + } + return 0; +} diff --git a/tools/vectorgen.cpp b/tools/vectorgen.cpp new file mode 100644 index 000000000..ff38ae4a5 --- /dev/null +++ b/tools/vectorgen.cpp @@ -0,0 +1,22 @@ +// Regenerates test-vectors/reference.txt. +// +// Build against the library whose behavior is to become the reference (the +// checked-in file was produced by the relic-based implementation) and +// redirect the output: +// +// ./build/src/vectorgen > test-vectors/reference.txt +// +// runtest compares the current library against the file; this tool only +// generates data. + +#include + +#include "test-vectors.hpp" + +int main() +{ + for (const std::string& line : bls_test_vectors::GenerateReferenceVectors()) { + printf("%s\n", line.c_str()); + } + return 0; +}