1
0
Fork 0
mirror of https://github.com/eosswedenorg/libantelope synced 2026-06-16 11:44:55 +02:00

Compare commits

...

161 commits

Author SHA1 Message Date
4787573a76 Version 0.2.2 2023-08-16 15:52:14 +02:00
11e086ee12 src/base58.cpp: need to include cstdint for uint8_t type 2023-08-16 15:49:12 +02:00
cadb1ff984 Rework README.md 2023-06-01 14:46:35 +02:00
1801c8424a Version 0.2.1 2023-05-31 15:06:58 +02:00
afc199e739 tests/CMakeLists.txt: use doctest_discover_tests() macro to add all doctest test cases with a separate CTest test. 2023-05-31 15:05:35 +02:00
12e779c8cf Adding tests/cmake/doctestAddTests.cmake 2023-05-31 15:03:19 +02:00
9ad2e3cc00 Adding tests/cmake/doctest.cmake 2023-05-31 15:03:07 +02:00
e15a5ede18 tests/hash: fix bug in MSVC compiler. 2023-05-31 14:42:20 +02:00
7e9bd41a83 src/libsecp256k1/rng.h: fix C467 warning on MSVC compilers. 2023-05-31 10:48:52 +02:00
e0d0bcb0cb include/libantelope/internal/hash.hpp: don't include OpenSSL headers here. instead we declare the state variables as plain arrays. 2023-05-31 10:47:44 +02:00
84645175c1 tests: Adding tests for sha256 and ripemd160 hashing functions. 2023-05-31 10:46:14 +02:00
93f6b1b030 Adding tests/include/testing.h 2023-05-31 10:45:25 +02:00
ec7a67ad88 src/openssl/hash.cpp: fix sha256_init() signature. 2023-05-31 10:42:12 +02:00
c035a804b2 src/wif/k1.cpp: implement _checksum_suffix() using init/update/final ripemd160 functions. 2023-05-30 13:59:43 +02:00
225a1947ae include/libantelope/hash.hpp: split into hash/ripemd160.hpp and hash/sha256.hpp 2023-05-30 13:55:07 +02:00
610c32c171 Hash: Define init/update/final functions for sha256 and ripemd160 2023-05-30 13:48:46 +02:00
891d2e970d Adding include/libantelope/internal/hash.hpp 2023-05-30 13:47:02 +02:00
be8096ed0f CMakeLists.txt: set LIBANTELOPE_HASHIMPL_OPENSSL 2023-05-30 13:46:42 +02:00
682ea069a2 CMakeLists.txt: use src/config.in.h as configuration file. 2023-05-30 13:46:23 +02:00
596cf8c774 Adding src/config.in.h 2023-05-30 13:45:35 +02:00
b7a1298615 src/libsecp256k1/ecdsa.cpp: fix unused variable warning in extended_nonce_function() 2023-04-10 17:32:59 +02:00
ee4705e858 src/libsecp256k1/ecdsa.cpp: fix warning about assigning an int value to unsigned char. 2023-04-10 17:31:52 +02:00
62b677d0e0 tests/base58/is_base58.cpp: fix integer size comparison warning. 2023-04-10 17:29:12 +02:00
000876176a src/base58.cpp: fix integer size comparison warning. 2023-04-10 17:25:25 +02:00
fa7d3cb2e9 src/ec.cpp: fix integer size comparison warning. 2023-04-10 17:25:25 +02:00
b7063f3f3a src/libsecp256k1/ecdsa.cpp: don't include "rng.h" here. 2023-04-10 17:22:04 +02:00
ac3facf30b src/WIF.cpp: fix integer comparison between different sizes warning. 2023-04-10 16:50:38 +02:00
7fc83991bc src/WIF.cpp: Remove unused variables. 2023-04-10 16:50:38 +02:00
c06846f7ed CMakeLists.txt: configure compiler flags for different build types. 2023-04-10 16:47:30 +02:00
a22825db9b README.md: fix some places where "libleap" was used instead of "libantelope" 2023-04-10 16:42:32 +02:00
3e38c587d3 Version 0.2.0 2023-04-06 14:49:48 +02:00
98a1ce7fcc src/libsecp256k1/rng.h: add header guard. 2023-04-06 14:48:17 +02:00
aa6bff9a84 cmake/libantelopeConfig.cmake.in: minor style fix. 2023-04-06 14:45:30 +02:00
923384025a cmake/libantelopeConfig.cmake.in: fix a typo in the documentation. 2023-04-06 14:45:30 +02:00
6824a2f49e Change namespace and header guards from libeosio to libantelope 2023-04-06 14:45:30 +02:00
0cfd459c71 Change project name from libeosio to libantelope 2023-04-06 14:45:30 +02:00
9d11357490 Version 0.1.7 2023-04-04 19:00:45 +02:00
424fe4702d include/libeosio/WIF.hpp: Make wif_priv_encode and wif_pub_encode default to K1 prefix. 2023-04-04 13:30:39 +02:00
2f56e8b43d include/libeosio/WIF.hpp: pass wif_codec_t to wif_print_key() and make it default to K1. 2023-04-04 13:30:07 +02:00
18f35c66b5 include/libeosio/WIF.hpp: add wif_codec_t struct and related functions/constants. 2023-04-04 13:29:12 +02:00
86d75df2ec include/libeosio/WIF.hpp: declare all WIF_ constant strings as extern and set them in WIF.cpp 2023-04-04 13:26:45 +02:00
33d3440f53 include/libeosio/checksum.hpp: Use plain c-array instead of std::array 2023-03-27 15:20:10 +02:00
abecabba99 include/libeosio/hash.hpp: remove nested struct from ripemd160_t and sha256_t and just use a array directly. 2023-03-26 19:28:01 +02:00
6026114869 Merge branch '3-support-new-key-format' into devel 2023-03-26 15:08:01 +02:00
815ab2569f src/wif/k1.cpp: in _checksum_suffix() change array to std::vector as MSVC does not like variable size c-arrays. 2023-03-25 19:54:05 +01:00
9819b2b94d src/WIF.cpp: use signature encoder/decoder. 2023-03-25 18:23:27 +01:00
be6c98f1bd src/wif/k1.cpp: implement sig_encoder_k1 and sig_decoder_k1 2023-03-25 18:23:27 +01:00
abdd84f257 src/wif/codec.hpp: Adding signature encoders/decoders 2023-03-25 18:23:27 +01:00
171db63de4 include/libeosio/WIF.hpp: Adding WIF_SIG_K1 constant. 2023-03-25 18:23:27 +01:00
1aa6906ba2 WIF: Support PVT_K1 format. 2023-03-25 18:23:27 +01:00
ea411793a2 src/WIF.cpp: use wif/codec.hpp 2023-03-25 16:02:09 +01:00
053f91c74b Adding src/wif/k1.cpp 2023-03-25 15:24:57 +01:00
6793762fbb Adding src/wif/legacy.cpp 2023-03-25 15:24:49 +01:00
3abfc488e8 Adding src/wif/codec.hpp 2023-03-25 15:24:05 +01:00
ebb4219024 WIF: Support PUB_K1 format. 2023-03-23 19:15:25 +01:00
9114c17315 include/libeosio/WIF.hpp: adding prefixes constants. 2023-03-23 19:14:51 +01:00
f8630de098 tests/WIF/pub_decode.cpp: fix test name. 2023-03-23 18:26:55 +01:00
f606f7e263 README.md: Update to reflect the use of libsecp256k1 2023-03-23 17:17:43 +01:00
2b68d7ec32 src/WIF.cpp: move _calculate_sig_checksum() to top of file, so we dont need to add a function declaration. 2023-03-23 14:19:37 +01:00
9ebc1e3aa1 Version 0.1.6 2023-03-22 19:04:47 +01:00
f936e1bbbc src/libsecp256k1/rng.h: Disable C4005 Warnings on windows temporarily. 2023-03-22 19:00:30 +01:00
e94b19031f CMakeLists.txt: when using libsecp256k1 on windows we need to link with bcrypt.lib 2023-03-22 18:53:33 +01:00
ea63d3e4ec CMakeLists.txt: Print ${EC_LIB} 2023-03-22 18:48:52 +01:00
c4304e3d22 CMakeLists.txt: move add_library() to top of "library" section so that we don't need to use variables for everything. 2023-03-22 18:48:25 +01:00
59e8ce473e tests/ec/ecdsa_recover.cpp: MSVC does not like "key: value" in initializer lists. so skip those. 2023-03-22 15:11:55 +01:00
e069c38a15 cmake/OpenSSL.cmake: Need to tell cmake that the imported openssl targets need to link with Crytp32 and ws2_32 libraries. 2023-03-22 15:09:42 +01:00
d9f7c41d8b .github/workflows/CI.yml: compile and test both openssl and libsecp256k1 as elliptic curve implementation. 2023-03-22 14:47:06 +01:00
f65a7ee6cd Merge branch 'libsecp256k1' into devel 2023-03-22 14:43:31 +01:00
91764f43de src/libsecp256k1/ecdsa.cpp: Adding ecdsa_recover() 2023-03-22 14:42:11 +01:00
53e126cb62 Adding src/libsecp256k1/ecdsa.cpp 2023-03-22 14:42:11 +01:00
259ca0f977 vendor/secp256k1/CMakeLists.txt: need to include recovery module for signatures. 2023-03-22 14:42:11 +01:00
760c50a655 src/libsecp256k1/ec.cpp: implement ec_generate_key() using ec_generqate_privkey() and ec_get_publickey() 2023-03-22 14:42:11 +01:00
b10911b21b src/libsecp256k1/ec.cpp: Implement ec_generate_privkey() and ec_get_publickey() 2023-03-22 14:42:11 +01:00
78d90b622e src/libsecp256k1/ec.cpp: ec_generate_key() should return 0 on success. 2023-03-22 14:42:11 +01:00
f920c7edee CMakeLists.txt: Add support for compiling with either openssl or libsecp256k1 as EC library 2023-03-22 14:42:11 +01:00
54fc8614ef Adding src/libsecp256k1/ec.cpp 2023-03-22 14:39:42 +01:00
2ab5e7b444 Adding src/libsecp256k1/rng.h 2023-03-22 14:39:42 +01:00
d737fe4762 Adding vendor/secp256k1/CMakeLists.txt 2023-03-22 14:39:42 +01:00
9587586968 move vendor/secp256k1-0.3.0 to vendor/secp256k1/repo 2023-03-22 14:39:42 +01:00
e8ee3fc0de Adding vendor/secp256k1-0.3.0 2023-03-22 14:39:42 +01:00
8a55500854 Merge branch 'openssl-ecdsa' into devel 2023-03-22 14:39:03 +01:00
d853ed6f62 Adding src/openssl/ecdsa.cpp 2023-03-22 14:38:03 +01:00
f0ebda6da5 src/openssl/internal.h: Adding signature serialize/unserialize function definitions. 2023-03-22 14:37:33 +01:00
4aa94ebee9 src/openssl/helpers.c: Adding signature serialize/unserialize functions. 2023-03-22 14:37:33 +01:00
073955ab50 src/openssl/internal.h: Adding EC_KEY_new_secp256k1 macro 2023-03-22 14:37:33 +01:00
d695d1c9ad src/openssl/internal.h: Adding ECDSA_SIG_recover_key_GFp function definition. 2023-03-22 14:21:17 +01:00
e4150af355 Adding src/openssl/recovery.c 2023-03-22 14:20:44 +01:00
88dacd22f8 src/openssl/ec.cpp: move helper functions/macros to internal.h and helpers.c files. 2023-03-22 14:19:36 +01:00
fdaab853ee CMakeLists.txt: add "C" to project languages. 2023-03-22 14:16:01 +01:00
b1da6552f7 src/openssl/ec.cpp: make calculate_pubkey() output an EC_POINT instead of encoding to binary, also pass group as a parameter. 2023-03-22 14:03:03 +01:00
7ead6f6bde src/openssl/ec.cpp: simplify EC_POINT_point2oct() with a macro. 2023-03-22 13:59:14 +01:00
fe873c1df8 tests/ec/ecdsa_sign.cpp: need to use ecdsa_verify() to check signatures.
different implementations produce different signatures so we can't use hardcoded values.
2023-03-22 13:46:28 +01:00
95740d47b3 tests/ec/ecdsa_verify.cpp: Adding more tests 2023-03-22 13:42:53 +01:00
a3efb5a5f2 src/openssl/ec.cpp: Remove unused variables. 2023-03-21 15:43:39 +01:00
15eaa97cec tests/ec: fix test names. 2023-03-21 15:34:26 +01:00
7ef801c2a8 include/libeosio/ec.hpp: Adding ecdsa_recover() 2023-03-21 13:28:37 +01:00
640f67a934 include/libeosio/ec.hpp: Adding ecdsa_sign and ecdsa_verify() 2023-03-21 13:28:30 +01:00
73819444e5 include/libeosio/WIF.hpp: adding signature encode/decode functions. 2023-03-20 13:48:35 +01:00
8a6b476a8b include/libeosio/ec.hpp: Adding signature type. 2023-03-20 13:48:35 +01:00
6eaf6ebe71 Adding tests/ec/generate.cpp 2023-03-20 13:47:04 +01:00
171876bbfa include/libeosio/ec.hpp: adding ec_generate_privkey() and ec_get_publickey() 2023-03-19 21:51:18 +01:00
4b8c1c7733 src/ec.cpp: in _hex() add comma between numbers. 2023-03-19 15:32:08 +01:00
d904fd38c3 src/ec.cpp: in _hex() append leading zero if value is <= 0xF 2023-03-19 15:32:08 +01:00
6c6c7e2329 src/openssl/ec.cpp: implement ec_init() and ec_shutdown() 2023-03-19 12:02:14 +01:00
b9c9e58050 include/libeosio/ec.hpp: Add ec_init() and ec_shutdown() functions. 2023-03-19 12:02:14 +01:00
0c9b867049 CMakeLists.txt: Should link to OpenSSL with PRIVATE scope instead of INTERFACE. otherwise include path to openssl will not be added to the target. 2023-03-15 12:15:04 +01:00
ab932b92e4 .github/workflows/CI.yml: disable fail-fast 2023-03-15 11:17:35 +01:00
07bee1e9e5 .github/workflows/CI.yml: Change cmake ENABLE_TESTING variable to BUILD_TESTING 2023-03-15 11:11:46 +01:00
6b0d1aa459 CMakeLists.txt: Adding WITH_BENCHMARK option. 2023-03-14 21:19:00 +01:00
d7cdf9476d CMakeLists.txt: CTest already defines "BUILD_TESTING" variable. so we do not have to use our own. 2023-03-14 21:18:05 +01:00
5f2fa977c3 Adding tests/benchmark/ec.cpp 2023-03-14 21:11:14 +01:00
de7f1c3e19 CMakeLists.txt: link with OpenSSL using imported target instead of variables. 2023-03-11 13:07:07 +01:00
9576e9d36f include/libeosio/hash.hpp: Include cstddef instead of cstdint (std::size_t should be more consistent with that header across platforms) 2023-03-11 10:52:29 +01:00
541eb089e8 CMakeLists.txt: typo fix. OpenSSL not OpenGL :) 2023-03-11 09:55:52 +01:00
2a2360bd7d Split include/libeosio/types.hpp into ec.hpp and hash.hpp 2023-03-11 09:48:12 +01:00
6a721c4634 README.md: Update the security notice to reflect that this is a library and not a program. 2023-03-10 14:08:04 +01:00
7be8afcd88 README.md: Drop support for ubuntu 18.04 2023-03-10 14:00:30 +01:00
9e41381798 .github/workflows/CI.yml: remove ubuntu 18.04 as it will be removed from github (see https://github.com/actions/runner-images/issues/6002) 2023-03-10 13:59:24 +01:00
70866da335 .github/workflows/package.yml: dont need to build twice for ubuntu 20.04 :) 2023-03-10 13:57:18 +01:00
0bc8fde4ad Version 0.1.5 2023-03-10 13:49:58 +01:00
c990bae52e include/libeosio/WIF.hpp: Adding wif_pub_decode function 2023-03-10 13:48:26 +01:00
65f93f061c tests/WIF: move structs local to test functions. 2023-03-10 13:27:10 +01:00
12a3be919a tests/base58/is_base58.cpp: add subcase and fix test structs. 2023-03-10 13:26:19 +01:00
e712d4ec4d tests/base58/decode.cpp: fix structs and move them local to test case function. 2023-03-10 13:21:54 +01:00
000f45e1f4 tests/base58/encode.cpp: add subcase and fix test structs. 2023-03-10 13:20:05 +01:00
3336f8d051 Adding tests/WIF/pub_encode.cpp 2023-03-10 13:10:07 +01:00
4015399d47 include/libeosio/WIF.hpp: Adding wif_priv_decode function. 2023-03-10 11:13:32 +01:00
82fc4b60cf include/libeosio/checksum.hpp: Adding checksum_validate function 2023-03-10 11:11:39 +01:00
a99a23ae9b include/libeosio/base58.hpp: Change decode function to accept vector instead of string. 2023-03-10 10:38:52 +01:00
ed59959b5b include/libeosio/base58.hpp: adding base58_decode functions. 2023-03-09 18:18:10 +01:00
a43b913277 tests: change from std::array to std::vector 2023-03-09 18:16:52 +01:00
cc962e7bf8 Adding tests/WIF/priv_encode.cpp 2023-03-09 18:16:52 +01:00
8764ecc4c1 .github/workflows/CI.yml: build and run tests. 2023-03-09 18:07:53 +01:00
dbbdc896e1 tests: Adding doctest framework. 2023-03-09 17:50:22 +01:00
35c2c2fe79 tests/base58/CMakeLists.txt: Use test function to define the tests. 2023-03-09 13:39:14 +01:00
47689bc621 tests/CMakeLists.txt: Define test function. 2023-03-09 13:38:51 +01:00
8ec58c385f tests: move base58 tests into its own subdirectory 2023-03-09 13:31:21 +01:00
bf73487abc include/libeosio/types.hpp: Adding stream operator functions. 2023-03-08 18:15:23 +01:00
77c93edc13 include/libeosio/WIF.hpp: Pass ec_privkey_t and ec_pubkey_t as const reference. 2023-03-08 17:24:22 +01:00
45c1f2909d LICENSE: Update year 2023-02-17 11:31:08 +01:00
c17512f6ba CMakeLists.txt: whitespace fix. 2023-02-17 11:24:20 +01:00
ae4dd4027f Add support for testing and add simple testcase for base58 2023-02-17 11:24:01 +01:00
d23aea0d19 README.md: Adding Supported Ubuntu versions 20.04 and 22.04 2022-09-23 12:52:38 +02:00
7f0e3ec5f7 .github/workflows/package.yml: Package for ubuntu 20.04 and 22.04 2022-09-23 12:52:04 +02:00
c5409589b5 .github/workflows/CI.yml: Build for Ubuntu 20.04 and 22.04 2022-09-23 12:29:54 +02:00
40b60c19be Version 0.1.4 2022-09-21 10:41:05 +02:00
a41b79967a CMakeLists.txt: Adding OPENSSL_API_COMPAT compiler flag. 2022-09-21 10:36:24 +02:00
569c1989a4 Version 0.1.3 2021-05-07 13:45:37 +02:00
3b7cb0f4f6 include/libeosio/base58.hpp: minor copy/paste error in comment. 2021-05-07 13:45:37 +02:00
2fa6220cea include/libeosio/ec.hpp: minor copy/paste error in comment. 2021-05-07 13:45:37 +02:00
0af91b4fc6 include/libeosio/hash.hpp: minor documentation fixes. 2021-05-07 13:45:37 +02:00
eab4aee8af WIF: add optional prefix parameter to wif_pub_encode() and wif_print_key(), defaults to "EOS" 2021-05-07 13:45:37 +02:00
19f6149e1e
README.md: Fixing a link 2021-04-03 11:59:02 +02:00
aa70efe4ea Version 0.1.2 2021-02-24 13:59:50 +01:00
56f056551e Update copyright year. 2021-02-24 13:58:47 +01:00
000f35cd9c src/WIF.cpp: include cstring instead of string.h 2021-02-24 13:56:55 +01:00
e7e92d2321 include/libeosio/WIF.hpp: need to include string 2021-02-24 13:51:58 +01:00
c785da97de Version 0.1.1 2020-05-22 16:18:04 +02:00
b619ab78d4 Adding/Updating comments in public header files. 2020-05-19 14:55:57 +02:00
45ae0b5791 README.md: Add information about what Linux distros/versions are officially supported. 2020-04-26 13:20:25 +02:00
010cd3a370 Github Actions: skip building/package for Ubuntu 16.04
16.04 only supports libssl 1.0 (officially).
If requested, we could consider supporting it but no point going through the hassle if it has no users.
2020-04-26 13:10:32 +02:00
189 changed files with 70761 additions and 252 deletions

View file

@ -10,10 +10,12 @@ on:
jobs:
compile:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-16.04, ubuntu-18.04, macos-latest, windows-latest ]
os: [ ubuntu-20.04, ubuntu-22.04, macos-latest, windows-latest ]
ec_lib: [ openssl, libsecp256k1 ]
name: ${{matrix.os}}
name: ${{matrix.os}} - ${{matrix.ec_lib}}
runs-on: ${{matrix.os}}
steps:
@ -30,12 +32,16 @@ jobs:
if [ "$RUNNER_OS" == "macOS" ]; then
SSL_OPTS="-D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1"
fi
cmake ${SSL_OPTS} -B build
cmake -DBUILD_TESTING=ON ${SSL_OPTS} -DEC_LIB=${{matrix.ec_lib}} -B build
- name: Build
shell: bash
run: cmake --build build
- name: Test
shell: bash
run: ctest --verbose --test-dir build -C Debug
- name: Upload artifact
uses: actions/upload-artifact@v1
with:

View file

@ -9,7 +9,7 @@ jobs:
ubuntu:
strategy:
matrix:
os: [ ubuntu-16.04, ubuntu-18.04 ]
os: [ ubuntu-20.04, ubuntu-22.04 ]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}
steps:
@ -62,7 +62,7 @@ jobs:
id: package
run: |
cmake --build build --config Release --target package
$FILE=(ls build/libeosio*.zip)
$FILE=(ls build/libantelope*.zip)
echo "::set-output name=filename::$FILE"
echo "::set-output name=name::$(([io.fileinfo]"$FILE").basename).zip"

View file

@ -5,11 +5,11 @@ cmake_minimum_required(VERSION 3.15)
# --------------------------------
# Project name and version
project(libeosio
VERSION 0.1.0
DESCRIPTION "C++ library for EOSIO"
HOMEPAGE_URL "https://github.com/eosswedenorg/libeosio"
LANGUAGES CXX
project(libantelope
VERSION 0.2.2
DESCRIPTION "C++ library for Antelope IO"
HOMEPAGE_URL "https://github.com/eosswedenorg/libantelope"
LANGUAGES C CXX
)
set( PROJECT_MAINTAINER "Henrik Hautakoski <henrik@eossweden.org>")
@ -20,6 +20,12 @@ set( PROJECT_MAINTAINER "Henrik Hautakoski <henrik@eossweden.org>")
set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/cmake" )
# --------------------------------
# Options
# --------------------------------
option(WITH_BENCHMARK "If tests are enabled (BUILD_TESTING variable), also build benchmark tree." OFF)
# --------------------------------
# Compiler
# --------------------------------
@ -28,44 +34,114 @@ set( CMAKE_CXX_STANDARD 11 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
set( CMAKE_CXX_EXTENSIONS OFF )
add_compile_options(
"$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wall;-Wconversion;-Wno-sign-conversion;-Wextra>"
"$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/W3;-D_CRT_SECURE_NO_WARNINGS=1>"
# Debug
"$<$<CONFIG:Debug>:$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-O0;-g>>"
"$<$<CONFIG:Debug>:$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/Od;/Zi>>"
# Release
"$<$<CONFIG:Release>:$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-O3>>"
"$<$<CONFIG:Release>:$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/O2>>"
# MinSizeRel
"$<$<CONFIG:MinSizeRel>:$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Os>>"
"$<$<CONFIG:MinSizeRel>:$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/O1>>"
)
# OpenSSL 3.0 deprecates some functions we use.
# Adding this flag makes the compiler not spam warnings.
add_compile_options(-D OPENSSL_API_COMPAT=0x10100000L)
# --------------------------------
# Options
# --------------------------------
set(EC_LIB "libsecp256k1" CACHE STRING "What elliptic curve implementation to use")
# --------------------------------
# Library
# --------------------------------
set( LIB_NAME ${PROJECT_NAME} )
set( LIB_SOURCE
add_library( ${LIB_NAME} STATIC
src/base58.cpp
src/ec.cpp
src/WIF.cpp
)
src/wif/k1.cpp
src/wif/legacy.cpp
# OpenSSL
include(OpenSSL)
set (LIB_SOURCE ${LIB_SOURCE}
src/openssl/ec.cpp
src/openssl/hash.cpp
)
add_library( ${LIB_NAME} STATIC ${LIB_SOURCE} )
target_include_directories( ${LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# Skip prefix on unix.
if (UNIX)
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
endif()
# OpenSSL
include(OpenSSL)
target_link_libraries( ${LIB_NAME} PRIVATE OpenSSL::Crypto)
target_link_libraries( ${LIB_NAME}
INTERFACE
${OPENSSL_CRYPTO_LIBRARY}
)
# Hash implementation
set( LIBANTELOPE_HASHIMPL_OPENSSL 1 )
target_include_directories( ${LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${OPENSSL_INCLUDE_DIR}
)
# EC Implementation
if (${EC_LIB} STREQUAL "libsecp256k1")
add_subdirectory( vendor/secp256k1 )
# Note: this is a big hack to get cmake to not export this library.
# Must be a better way, but works so cba.
target_include_directories( ${LIB_NAME}
PRIVATE $<BUILD_INTERFACE:$<TARGET_PROPERTY:secp256k1,INCLUDE_DIRECTORIES>>
)
target_sources( ${LIB_NAME} PRIVATE
$<TARGET_OBJECTS:secp256k1>
src/libsecp256k1/ec.cpp
src/libsecp256k1/ecdsa.cpp
)
# Need to link to bcrypt on windows as BCryptGenRandom is
# used by fill_random in src/libsecp256k1/rng.h
if (WIN32)
target_link_libraries( ${LIB_NAME} PRIVATE "bcrypt.lib" )
endif (WIN32)
elseif (${EC_LIB} STREQUAL "openssl")
target_sources( ${LIB_NAME} PRIVATE
src/openssl/ec.cpp
src/openssl/ecdsa.cpp
src/openssl/helpers.c
src/openssl/recovery.c
)
else()
message(FATAL_ERROR "Invalid ec implementation: " ${EC_LIB})
endif()
message("-- Using Elliptic curve library: ${EC_LIB}")
# Generate config file
configure_file(src/config.in.h ${CMAKE_BINARY_DIR}/include/libantelope/config.h)
# --------------------------------
# Tests
# --------------------------------
include(CTest)
if (BUILD_TESTING)
# enable testing functionality
enable_testing()
add_subdirectory( tests )
endif (BUILD_TESTING)
# --------------------------------
# Install
@ -91,7 +167,7 @@ install(TARGETS ${LIB_NAME} EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY include/ ${CMAKE_BINARY_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# Readme and license
install(FILES README.md LICENSE LICENSE.bitcoin

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019-2020 EOS Sw/eden
Copyright (c) 2019-2023 EOS Sw/eden
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

110
README.md
View file

@ -1,114 +1,110 @@
![](https://github.com/eosswedenorg/libeosio/workflows/CI/badge.svg)
[![GitHub release](https://img.shields.io/github/v/release/eosswedenorg/libeosio?include_prereleases)](https://github.com/eosswedenorg/libeosio/releases/latest)
![](https://github.com/eosswedenorg/libantelope/workflows/CI/badge.svg)
[![GitHub release](https://img.shields.io/github/v/release/eosswedenorg/libantelope?include_prereleases)](https://github.com/eosswedenorg/libantelope/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
# libeosio
# libantelope
Independent C++ library for [EOS](https://eos.io/)
NOTE: This repository has no connection to the official EOS code.
libantelope is an independent C++ library designed for Antelope IO (formerly known as libeosio). Please note that this repository is not affiliated with the official Antelope code.
## Compiling the library
You will need `openssl` development files (version 1.1 or later) to compile and `cmake 3.15` or later to compile this project.
To compile this project, you will need the following:
- `openssl` development files (version 1.1 or later)
- `cmake 3.15` or later
### Elliptic curve backend
The library offers two different backend implementations for the elliptic curve functionality:
- `libsecp256k1`
- `OpenSSL`: Although the default is to use `libsecp256k1` for optimization, you still need to link to OpenSSL as other parts of the codebase rely on it.
To switch the implementation, modify the `EC_LIB` variable in the cmake.
### CMake
You can install `cmake` by reading the [official guide](https://cmake.org/install).
You can install `cmake` by referring to the [official guide](https://cmake.org/install).
### Linux
**NOTE:** Only Ubuntu versions `20.04` and `22.04` are officially supported.
While the project should compile fine on most versions/distros, it is only tested and distributed for Ubuntu `20.04` and `22.04` by [Sw/eden](https://www.eossweden.org).
#### Dependencies
**Ubuntu (or other debian based distros)**
**Ubuntu (or other Debian-based distros)**
First you need to have a compiler, `openssl` and `cmake`. this can be installed with apt.
To install the necessary dependencies (compiler, `openssl`, and `cmake`), use the following `apt` command:
```sh
$ apt-get install gcc g++ cmake libssl-dev
apt-get install gcc g++ cmake libssl-dev
```
If you need a newer version of cmake then apt provides.
Checkout the official [CMake APT repository](https://apt.kitware.com/).
If you require a newer version of `cmake`, you can refer to the [official CMake APT repository](https://apt.kitware.com/).
**Other**
Consult your package manager's manual for getting `openssl`,`g++` and `cmake` installed.
If you need a newer version of cmake then your package manager provides. checkout the [official guide](https://cmake.org/install).
For other distros, please consult your package manager's manual to install `openssl`, `g++`, and `cmake`. If you need a newer version of `cmake`, you can follow the [official installation guide](https://cmake.org/install).
### MacOS
#### Dependencies
You must have a compiler installed. This project is known to build with `Xcode 11.0` but other versions should work.
Ensure that you have a compiler installed. This project is known to build with `Xcode 11.0`, but other versions should work as well.
To install `openssl` and `cmake`, you can use the following `brew` command:
You need to have openssl and cmake installed also, this can be done with this `brew` command:
```sh
$ brew install openssl cmake
brew install openssl cmake
```
If you need a newer version of cmake then brew provides. checkout the [official guide](https://cmake.org/install)
If you require a newer version of `cmake`, refer to the [official installation guide](https://cmake.org/install).
#### Build
```sh
$ mkdir build && cd build
$ cmake .. && make
mkdir build && cd build
cmake .. && make
```
**MacOS:** You may need to point `cmake` to `openssl` by passing the argument
`-D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1` if openssl is not under `/usr/local/opt/openssl@1.1` you need to find the correct path.
**MacOS:** If your `openssl` installation is not located at `/usr/local/opt/openssl@1.1`, you may need to pass the argument `-D OPENSSL_ROOT_DIR=/path/to/openssl` to `cmake` and specify the correct path.
### Windows
#### Dependencies
First you will need a compiler.
First, ensure that you have a compiler installed.
[Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (Selecting C++ during installation) is recommended.
It is recommended to use [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) and select C++ during installation.
By default `cmake` will use the bundled openssl package located at `vendor/openssl-1.1.1e-win-static.zip`
By default, `cmake` will utilize the bundled OpenSSL package located at `vendor/openssl-1.1.1e-win-static.zip`. If you prefer to use a different version of OpenSSL, set the `OPENSSL_ROOT_DIR` to the directory where OpenSSL is located on your system:
If you like to use an other version of OpenSSL then the static one bundled with this repo
you need to set `OPENSSL_ROOT_DIR` to the directory where OpenSSL is located on the system.
For example:
```
C:\repo> cmake -D OPENSSL_ROOT_DIR=C:/path/to/openssl -B build
```sh
cmake -D OPENSSL_ROOT_DIR=C:/path/to/openssl -B build
```
**NOTE:** `cmake` uses forward slash `/` for path even for windows. so make sure you use that when setting `OPENSSL_ROOT_DIR`
**NOTE:** `cmake` uses forward slashes `/` for paths, even on Windows, so ensure that you use them when setting
#### Build.
`OPENSSL_ROOT_DIR`.
Run cmake
#### Build
```
C:\repo> cmake -B build
C:\repo> cmake --build build --config Release
Run `cmake`:
```sh
cmake -B build
cmake --build build --config Release
```
## Security notice
## Security Notice
Keys are generated by `OpenSSL`'s `EC_KEY_generate_key` function. The program will
never expose your keys to anything but the computers memory and output of the
program. You are free to inspect the source code and compile yourself to verify.
The library performs elliptic curve cryptographic operations using either the `OpenSSL` or `libsecp256k1` libraries. The `libantelope` library ensures that sensitive cryptographic information is only stored in computer memory and not exposed to external sources. You are encouraged to inspect the source code and compile it yourself for verification purposes.
However, use this at your own risk. we cannot guarantee that the keys are
cryptographically secure as this depends on OpenSSL's implementation (alto it is
widely used and should be safe)
However, please use this library at your own risk. While both OpenSSL and libsecp256k1 are widely used and considered safe, we cannot guarantee the cryptographic security of the keys as it depends on the elliptic curve implementation.
Please read the `LICENSE` file.
```
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
Please refer to the `LICENSE` file for more information.
## Author

View file

@ -24,4 +24,5 @@ find_package(OpenSSL 1.1 REQUIRED)
# Bug in FindOpenSSL. Win needs to link to these if static libs are used.
if (WIN32 AND OPENSSL_USE_STATIC_LIBS)
set (OPENSSL_CRYPTO_LIBRARY "${OPENSSL_CRYPTO_LIBRARY};Crypt32;ws2_32")
target_link_libraries(OpenSSL::Crypto INTERFACE "Crypt32;ws2_32")
endif()

View file

@ -0,0 +1,26 @@
# This script provides the libantelope as an import target
# ----------------------------------------------------------
#
# Use find_package() so cmake will find libantelope:
#
# find_package(libantelope) # No specific version
# find_package(libantelope REQUIRED) # No specific version, but the library must be found.
# find_package(libantelope 0.1) # any 0.1.x, but the library is optional.
# find_package(libeantelope 0.1.0) # 0.1.0 or greater, but the library is optional.
#
# Then you just link the you target with libantelope target:
#
# target_link_libraries( ${PROGRAM_EXE} PUBLIC libantelope )
#
# if you do not specify REQUIRED. you must check the variable libantelope_FOUND
# and and only link to it if it's defined:
#
# if (libantelope_FOUND)
# ...
# target_link_libraries( ${PROGRAM_EXE} PUBLIC libantelope )
# ..
# endif()
set( LIBANTELOPE_VERSION "@PROJECT_VERSION@" )
include ( "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake" )

View file

@ -1,26 +0,0 @@
# This script provides the libeosio as an import target
# ----------------------------------------------------------
#
# Use find_package() so cmake will find libeosio:
#
# find_package(libeosio) # No specific version
# find_package(libeosio REQUIRED) # No specific version, but the library must be found.
# find_package(libeosio 0.1) # any 0.1.x, but the library is optional.
# find_package(libeosio 0.1.0) # 0.1.0 or greater, but the library is optional.
#
# Then you just link the you target with eoskeygen target:
#
# target_link_libraries( ${PROGRAM_EXE} PUBLIC libeosio )
#
# if you do not specify REQUIRED. you must check the variable libeosio_FOUND
# and and only link to it if it's defined:
#
# if (libeosio_FOUND)
# ...
# target_link_libraries( ${PROGRAM_EXE} PUBLIC libeosio )
# ..
# endif()
set(LIBEOSIO_VERSION "@PROJECT_VERSION@")
include ( "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake" )

View file

@ -0,0 +1,99 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBANTELOPE_WIF_H
#define LIBANTELOPE_WIF_H
#include <string>
#include <libantelope/ec.hpp>
namespace libantelope {
/**
* Key prefixes. (strings that is not equal to these prefixes are treated as legacy format.)
*/
extern const std::string WIF_PUB_LEG;
extern const std::string WIF_PUB_K1;
extern const std::string WIF_PVT_LEG;
extern const std::string WIF_PVT_K1;
extern const std::string WIF_SIG_K1;
/**
* Codecs
*/
// A WIF Codec is an public and private key prefix pair.
typedef struct {
std::string pub;
std::string pvt;
} wif_codec_t;
extern const wif_codec_t WIF_CODEC_K1;
extern const wif_codec_t WIF_CODEC_LEG;
inline wif_codec_t wif_create_legacy_codec(const std::string& pub_prefix) {
return { pub_prefix, WIF_PVT_LEG };
}
/**
* Encode an EC private key to WIF String.
*/
std::string wif_priv_encode(const ec_privkey_t& priv, const std::string& prefix = WIF_PVT_K1);
/**
* Decode an WIF String to EC private key
*/
bool wif_priv_decode(ec_privkey_t& priv, const std::string& data);
/**
* Encode an EC public key to WIF String.
*/
std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix = WIF_PUB_K1);
/**
* Decode an WIF String to EC public key
*/
bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data);
/**
* Prints an EC keypair in WIF format to standard out.
*/
void wif_print_key(const struct ec_keypair *key, const wif_codec_t& codec = WIF_CODEC_K1);
/**
* Signatures
*/
/**
* Encode an EC signature to WIF String.
*/
std::string wif_sig_encode(const ec_signature_t& sig);
/**
* Decode an WIF String to EC signature
*/
bool wif_sig_decode(ec_signature_t& sig, const std::string& data);
} // namespace libantelope
#endif /* LIBANTELOPE_WIF_H */

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,26 +21,46 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_BASE58_H
#define LIBEOSIO_BASE58_H
#ifndef LIBANTELOPE_BASE58_H
#define LIBANTELOPE_BASE58_H
#include <string>
#include <vector>
namespace libeosio {
namespace libantelope {
/**
* Base58 Encoding functions.
*/
std::string base58_encode(const std::string& str);
std::string base58_encode(const std::vector<unsigned char>& vch);
std::string base58_encode(const unsigned char* pbegin, const unsigned char* pend);
/**
* Base58 Decoding functions.
*/
bool base58_decode(const char* psz, std::vector<unsigned char>& out);
bool base58_decode(const std::string& str, std::vector<unsigned char>& out);
/**
* Returns true if `ch` is a base58 character, false otherwise.
*/
bool is_base58(char ch);
// Returns std::string::npos if the string contains only base58 characters
// Otherwise the position of the first non base58 character is returned.
/**
* Returns std::string::npos if the string contains only base58 characters
* Otherwise the position of the first non base58 character is returned.
*/
size_t is_base58(const std::string& str);
/**
* Strips all non-base58 characters from `str`.
* The string is modified in place and the same string is
* returned without non-base58 chars.
*/
std::string& base58_strip(std::string& str);
} //namespace eoskeygen
} //namespace libantelope
#endif /* LIBEOSIO_BASE58_H */
#endif /* LIBANTELOPE_BASE58_H */

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,34 +21,53 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_CHECKSUM_H
#define LIBEOSIO_CHECKSUM_H
#ifndef LIBANTELOPE_CHECKSUM_H
#define LIBANTELOPE_CHECKSUM_H
#include <cstdint>
#include <cstring>
#include <array>
#include <libeosio/hash.hpp>
#include <libantelope/hash.hpp>
namespace libeosio {
namespace libantelope {
/**
* Checksum size (in bytes)
*/
#define CHECKSUM_SIZE 4
typedef std::array<unsigned char, CHECKSUM_SIZE> checksum_t;
/**
* Checksum datatype
*/
typedef unsigned char checksum_t[CHECKSUM_SIZE];
/**
* Checksum template function.
* Template arguments:
* - T: Hash type.
* - F: Hash calculation function, should have the signature `T* F(const unsigned char *, std::size_t, T*)`
*/
template <typename T, T* (*F)(const unsigned char *, std::size_t, T*)>
inline checksum_t checksum(const unsigned char* data, std::size_t len) {
checksum_t crc;
inline void checksum(const unsigned char* data, std::size_t len, checksum_t crc) {
T hash;
F(data, len, &hash);
std::memcpy(crc.data(), &hash, crc.size());
return crc;
std::memcpy(crc, &hash, CHECKSUM_SIZE);
}
template <void (*F)(const unsigned char *, std::size_t, checksum_t)>
inline bool checksum_validate(const unsigned char* data, std::size_t len) {
checksum_t crc;
F(data, len - CHECKSUM_SIZE, crc);
return !memcmp(crc, data + (len - CHECKSUM_SIZE), CHECKSUM_SIZE);
}
/**
* Checksum implementations.
*/
#define checksum_sha256 checksum<sha256_t, sha256>
#define checksum_sha256d checksum<sha256_t, sha256d>
#define checksum_ripemd160 checksum<ripemd160_t, ripemd160>
} // namespace libeosio
} // namespace libantelope
#endif /* LIBEOSIO_CHECKSUM_H */
#endif /* LIBANTELOPE_CHECKSUM_H */

140
include/libantelope/ec.hpp Normal file
View file

@ -0,0 +1,140 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBANTELOPE_EC_H
#define LIBANTELOPE_EC_H
#include <libantelope/hash.hpp>
#include <iostream>
#include <array>
namespace libantelope {
/**
* Elliptic curve private key size (in bytes)
*/
#define EC_PRIVKEY_SIZE 32
/**
* Elliptic curve public key size (in bytes)
*
* Compressed format: z||x, where byte z specifies which (of the 2) solutions
* of the quadratic equation y is. Each cordinate is 32 bytes.
*/
#define EC_PUBKEY_SIZE (32 + 1)
/**
* Elliptic curve priv/pub key datastructures.
*/
typedef std::array<unsigned char, EC_PRIVKEY_SIZE> ec_privkey_t;
typedef std::array<unsigned char, EC_PUBKEY_SIZE> ec_pubkey_t;
/**
* Elliptic curve keypair (public + private)
*/
struct ec_keypair {
ec_privkey_t secret;
ec_pubkey_t pub;
};
/**
* Elliptic curve recoverable signature
*
* The signature consist of 2 integers r,s and v where
* r: x cordinate of the random point
* s: signature proof
* v: recovery id (0, 1, 2 or 3), eg. what EC point is the public key.
*
* The memory layout is as follows:
* r(32), s(32), v(1) = 65 bytes.
*/
/**
* Elliptic curve signature key size (in bytes)
*/
#define EC_SIGNATURE_SIZE (32 + 32 + 1)
/**
* Elliptic curve signature datastructure.
*/
typedef std::array<unsigned char, EC_SIGNATURE_SIZE> ec_signature_t;
/**
* Initialize the ec library.
*/
int ec_init();
/**
* Generates an new random private key using the secp256k1 curve.
*/
int ec_generate_privkey(ec_privkey_t *priv);
/**
* Get the public key from an private key.
*/
int ec_get_publickey(const ec_privkey_t *priv, ec_pubkey_t* pub);
/**
* Generates a keypair using the secp256k1 curve.
* public key is in compressed format.
*/
int ec_generate_key(struct ec_keypair *pair);
/**
* Sign
*/
/**
* Create a ECDSA signature, returns -1 if an error occured or zero on success.
*/
int ecdsa_sign(const ec_privkey_t& key, const sha256_t* digest, ec_signature_t& sig);
/**
* Verify an ECDSA signature,
* returns zero if the signature is correct. -1 if the signature is incorrect or an error occured.
*/
int ecdsa_verify(const sha256_t* digest, const ec_signature_t& sig, const ec_pubkey_t& key);
/**
* Recover the public key from the signature.
* returns zero if the public key could be extracted. -1 if an error occured.
*/
int ecdsa_recover(const sha256_t* digest, const ec_signature_t& sig, ec_pubkey_t& key);
/**
* Shutdown the ec library.
*/
void ec_shutdown();
} // namespace libantelope
// Stream operators
std::ostream& operator<<(std::ostream& os, const libantelope::ec_privkey_t& pk);
std::ostream& operator<<(std::ostream& os, const libantelope::ec_pubkey_t& pk);
#endif /* LIBANTELOPE_EC_H */

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,19 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_EC_H
#define LIBEOSIO_EC_H
#ifndef LIBANTELOPE_HASH_H
#define LIBANTELOPE_HASH_H
#include <libeosio/types.hpp>
#include <libantelope/hash/sha256.hpp>
#include <libantelope/hash/ripemd160.hpp>
namespace libeosio {
/**
* Generates a keypair using the secp256k1 curve.
* public key is in compressed format.
*/
int ec_generate_key(struct ec_keypair *pair);
} // namespace eoskeygen
#endif /* LIBEOSIO_EC_H */
#endif /* LIBANTELOPE_HASH_H */

View file

@ -0,0 +1,62 @@
/**
* MIT License
*
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBANTELOPE_HASH_RIPEMD160_H
#define LIBANTELOPE_HASH_RIPEMD160_H
#include <cstddef>
#include <libantelope/internal/hash.hpp>
namespace libantelope {
typedef unsigned char ripemd160_t[20];
typedef internal::ripemd160_state ripemd160_ctx_t;
/**
* Initialize a ripmemd160_ctx_t structure
*/
int ripemd160_init(ripemd160_ctx_t* ctx);
/**
* Update the RipeMD160 hash value with the contents in `data` up to `len` bytes.
* This can be called repeatedly to hash chunks of data.
*/
int ripemd160_update(ripemd160_ctx_t* ctx, const void *data, std::size_t len);
/**
* Places the RipeMD160 message digest in out variable.
* The ctx's internal state is reset after this operation.
*/
int ripemd160_final(ripemd160_ctx_t* ctx, ripemd160_t* out);
/**
* RipeMD160 hashing function.
* Hashes the content in `data` up to `len` bytes. The result is stored in `out`.
* Returns the same pointer as `out`.
*/
ripemd160_t* ripemd160(const unsigned char *data, std::size_t len, ripemd160_t* out);
} // namespace libantelope
#endif /* LIBANTELOPE_RIPEMD160_H */

View file

@ -0,0 +1,69 @@
/**
* MIT License
*
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBANTELOPE_HASH_SHA256_H
#define LIBANTELOPE_HASH_SHA256_H
#include <cstddef>
#include <libantelope/internal/hash.hpp>
namespace libantelope {
typedef unsigned char sha256_t[32];
typedef internal::sha256_state sha256_ctx_t;
/**
* Initialize a sha256_ctx_t structure
*/
int sha256_init(sha256_ctx_t* ctx);
/**
* Update the sha256 hash value with the contents in `data` up to `len` bytes.
* This can be called repeatedly to hash chunks of data.
*/
int sha256_update(sha256_ctx_t* ctx, const void *data, std::size_t len);
/**
* Place the message digest in out variable.
* The ctx's internal state is reset after this operation.
*/
int sha256_final(sha256_ctx_t* ctx, sha256_t* out);
/**
* sha256 hashing function.
* Hashes the content in `data` up to `len` bytes. The result is stored in `out`.
* Returns the same pointer as `out`.
*/
sha256_t* sha256(const unsigned char *data, std::size_t len, sha256_t* out);
/**
* sha256 double hashing function.
* Hashes the content in `data` up to `len` bytes. The result is stored in `out`.
* Returns the same pointer as `out`.
*/
sha256_t* sha256d(const unsigned char *data, std::size_t len, sha256_t* out);
} // namespace libantelope
#endif /* LIBANTELOPE_HASH_SHA256_H */

View file

@ -1,7 +1,8 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,21 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_HASH_H
#define LIBEOSIO_HASH_H
#ifndef LIBANTELOPE_INTERNAL_HASH_H
#define LIBANTELOPE_INTERNAL_HASH_H
#include <cstdint>
#include <libeosio/types.hpp>
#include <libantelope/config.h>
namespace libeosio {
#ifdef LIBANTELOPE_HASHIMPL_OPENSSL
sha256_t* sha256(const unsigned char *data, std::size_t len, sha256_t* out);
namespace libantelope { namespace internal {
// sha256 double.
sha256_t* sha256d(const unsigned char *data, std::size_t len, sha256_t* out);
typedef unsigned char sha256_state[112];
typedef unsigned char ripemd160_state[96];
} } // namespace libantelope::internal
ripemd160_t* ripemd160(const unsigned char *data, std::size_t len, ripemd160_t* out);
#else
#error "Missing hash implementation"
#endif
} // namespace libeosio
#endif /* LIBEOSIO_HASH_H */
#endif /* LIBANTELOPE_INTERNAL_INTERNAL_H */

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -22,45 +22,134 @@
* SOFTWARE.
*/
#include <iostream>
#include <string.h>
#include <libeosio/base58.hpp>
#include <libeosio/checksum.hpp>
#include <libeosio/WIF.hpp>
#include <cstring>
#include <libantelope/base58.hpp>
#include <libantelope/checksum.hpp>
#include <libantelope/WIF.hpp>
#include "wif/codec.hpp"
namespace libeosio {
namespace libantelope {
#define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */
const std::string WIF_PUB_LEG = "EOS";
const std::string WIF_PUB_K1 = "PUB_K1_";
const std::string WIF_PVT_LEG = "";
const std::string WIF_PVT_K1 = "PVT_K1_";
const std::string WIF_SIG_K1 = "SIG_K1_";
std::string wif_priv_encode(ec_privkey_t priv) {
const wif_codec_t WIF_CODEC_K1 = { WIF_PUB_K1, WIF_PVT_K1 };
const wif_codec_t WIF_CODEC_LEG = wif_create_legacy_codec(WIF_PUB_LEG);
checksum_t check;
// 1 byte extra for prefix.
unsigned char buf[1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE] = { PRIV_KEY_PREFIX };
std::string wif_priv_encode(const ec_privkey_t& priv, const std::string& prefix) {
memcpy(buf + 1, priv.data(), priv.size());
// 1 byte extra for legacy prefix prefix.
unsigned char buf[1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE] = { 0 };
size_t len;
// Checksum
check = checksum_sha256d(buf, 1 + EC_PRIVKEY_SIZE);
memcpy(buf + 1 + EC_PRIVKEY_SIZE, check.data(), check.size());
if (prefix == WIF_PVT_K1) {
len = internal::priv_encoder_k1(priv, buf);
} else if (prefix == WIF_PVT_LEG) {
len = internal::priv_encoder_legacy(priv, buf);
} else {
return "";
}
return base58_encode(buf, buf + sizeof(buf));
return prefix + base58_encode(buf, buf + len);
}
std::string wif_pub_encode(ec_pubkey_t pub) {
bool wif_priv_decode(ec_privkey_t& priv, const std::string& data) {
std::size_t offset;
std::vector<unsigned char> buf;
internal::priv_decoder_t decoder = internal::priv_decoder_legacy;
// Check prefix
if (data.substr(0, WIF_PVT_K1.size()) == WIF_PVT_K1) {
offset = WIF_PVT_K1.size();
decoder = internal::priv_decoder_k1;
} else {
// Legacy
offset = 0;
}
if (!base58_decode(data.c_str() + offset, buf)) {
return false;
}
return decoder(buf, priv);
}
std::string wif_pub_encode(const ec_pubkey_t& pub, const std::string& prefix) {
checksum_t check = checksum_ripemd160(pub.data(), pub.size());
unsigned char buf[EC_PUBKEY_SIZE + CHECKSUM_SIZE];
internal::pub_encoder_t encoder;
memcpy(buf, pub.data(), pub.size());
memcpy(buf + EC_PUBKEY_SIZE, check.data(), check.size());
if (prefix == WIF_PUB_K1) {
encoder = internal::pub_encoder_k1;
}
// Legacy
else {
encoder = internal::pub_encoder_legacy;
}
return "EOS" + base58_encode(buf, buf + sizeof(buf));
encoder(pub, buf);
return prefix + base58_encode(buf, buf + sizeof(buf));
}
void wif_print_key(const struct ec_keypair *key) {
bool wif_pub_decode(ec_pubkey_t& pub, const std::string& data) {
std::cout << "Public: " << wif_pub_encode(key->pub) << std::endl;
std::cout << "Private: " << wif_priv_encode(key->secret) << std::endl;
internal::pub_decoder_t decoder = internal::pub_decoder_legacy;
std::size_t offset;
std::vector<unsigned char> buf;
// Check prefix
if (data.substr(0, WIF_PUB_K1.size()) == WIF_PUB_K1) {
decoder = internal::pub_decoder_k1;
offset = WIF_PUB_K1.size();
} else {
// Legacy
offset = 3;
}
if (!base58_decode(data.c_str() + offset, buf)) {
return false;
}
if (buf.size() != EC_PUBKEY_SIZE + CHECKSUM_SIZE) {
return false;
}
return decoder(buf, pub);
}
} // namespace libeosio
void wif_print_key(const struct ec_keypair *key, const wif_codec_t& codec) {
std::cout << "Public: " << wif_pub_encode(key->pub, codec.pub) << std::endl;
std::cout << "Private: " << wif_priv_encode(key->secret, codec.pvt) << std::endl;
}
bool wif_sig_decode(ec_signature_t& sig, const std::string& data) {
std::vector<unsigned char> buf;
if (data.substr(0, WIF_SIG_K1.length()) != WIF_SIG_K1) {
// Invalid prefix
return false;
}
if (!base58_decode(data.c_str() + WIF_SIG_K1.length(), buf)) {
return false;
}
return internal::sig_decoder_k1(buf, sig);
}
std::string wif_sig_encode(const ec_signature_t& sig) {
unsigned char buf[EC_SIGNATURE_SIZE + CHECKSUM_SIZE];
internal::sig_encoder_k1(sig, buf);
return WIF_SIG_K1 + base58_encode(buf, buf + sizeof(buf));
}
} // namespace libantelope

View file

@ -25,13 +25,38 @@
* Based on code from https://github.com/bitcoin/bitcoin/blob/f1e2f2a85962c1664e4e55471061af0eaa798d40/src/base58.cpp
*/
#include <algorithm>
#include <cstdint>
#include <cstddef>
#include <cassert>
#include <libeosio/base58.hpp>
#include <cstring>
#include <libantelope/base58.hpp>
namespace libeosio {
namespace libantelope {
static const char charmap[59] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const int8_t table[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8,-1,-1,-1,-1,-1,-1,
-1, 9,10,11,12,13,14,15, 16,-1,17,18,19,20,21,-1,
22,23,24,25,26,27,28,29, 30,31,32,-1,-1,-1,-1,-1,
-1,33,34,35,36,37,38,39, 40,41,42,43,-1,44,45,46,
47,48,49,50,51,52,53,54, 55,56,57,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
bool is_space(char c) {
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
}
std::string base58_encode(const unsigned char* pbegin, const unsigned char* pend) {
@ -84,6 +109,62 @@ std::string base58_encode(const std::vector<unsigned char>& vch) {
return base58_encode(vch.data(), vch.data() + vch.size());
}
bool base58_decode(const char* psz, std::vector<unsigned char>& out) {
// Skip leading spaces.
while (*psz && is_space(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
int length = 0;
while (*psz == '1') {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
std::size_t size = strlen(psz) * 733 /1000 + 1; // log(58) / log(256), rounded up.
std::vector<unsigned char> b256(size);
// Process the characters.
while (*psz && !is_space(*psz)) {
// Decode base58 character
int carry = table[(uint8_t)*psz];
if (carry == -1) // Invalid b58 character
return false;
int i = 0;
for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); (carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) {
carry += 58 * (*it);
*it = (unsigned char) (carry % 256);
carry /= 256;
}
assert(carry == 0);
length = i;
psz++;
}
// Skip trailing spaces.
while (is_space(*psz))
psz++;
if (*psz != 0)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::iterator it = b256.begin() + (size - length);
while (it != b256.end() && *it == 0)
it++;
// Copy result into output vector.
out.reserve(zeroes + (b256.end() - it));
out.assign(zeroes, 0);
while (it != b256.end())
out.push_back(*(it++));
return true;
}
bool base58_decode(const std::string& str, std::vector<unsigned char>& out) {
return base58_decode(str.c_str(), out);
}
bool is_base58(char ch) {
for(unsigned int i=0; i < sizeof(charmap); i++) {
if (ch == charmap[i]) {
@ -110,4 +191,4 @@ std::string& base58_strip(std::string &str) {
return str;
}
} // namespace libeosio
} // namespace libantelope

View file

@ -1,7 +1,7 @@
/**
/*
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,19 +21,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_WIF_H
#define LIBEOSIO_WIF_H
#ifndef LIBANTELOPE_CONFIG_H
#define LIBANTELOPE_CONFIG_H
#include <libeosio/types.hpp>
#ifdef __cplusplus
extern "C" {
#endif
namespace libeosio {
/* Hash implementation */
#cmakedefine LIBANTELOPE_HASHIMPL_OPENSSL
std::string wif_priv_encode(ec_privkey_t priv);
#ifdef __cplusplus
}
#endif
std::string wif_pub_encode(ec_pubkey_t pub);
void wif_print_key(const struct ec_keypair *key);
} // namespace libeosio
#endif /* LIBEOSIO_WIF_H */
#endif /* LIBANTELOPE_CONFIG_H */

47
src/ec.cpp Normal file
View file

@ -0,0 +1,47 @@
/**
* MIT License
*
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <libantelope/ec.hpp>
std::ostream& _hex(std::ostream& os, const unsigned char *b, std::size_t sz) {
os << "[ " << std::hex;
for (std::size_t i = 0; i < sz; i++) {
unsigned int v = b[i];
os << "0x";
if (v <= 0xF) {
os << "0";
}
os << v;
if (i < sz-1) os << ", ";
}
return os << std::oct << " ]";
}
std::ostream& operator<<(std::ostream& os, const libantelope::ec_privkey_t& k) {
return _hex(os, k.data(), k.size());
}
std::ostream& operator<<(std::ostream& os, const libantelope::ec_pubkey_t& k) {
return _hex(os, k.data(), k.size());
}

93
src/libsecp256k1/ec.cpp Normal file
View file

@ -0,0 +1,93 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <secp256k1.h>
#include <secp256k1_ecdh.h>
#include <libantelope/ec.hpp>
#include "rng.h"
namespace libantelope {
secp256k1_context* ctx;
int ec_init() {
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
return ctx == NULL ? -1 : 0;
}
void ec_shutdown() {
if (ctx) {
secp256k1_context_destroy(ctx);
ctx = NULL;
}
}
int ec_generate_privkey(ec_privkey_t *priv) {
unsigned char randomize[32];
if (!fill_random(randomize, sizeof(randomize))) {
return -1;
}
if (secp256k1_context_randomize(ctx, randomize) < 0) {
return -1;
}
while (1) {
if (!fill_random(priv->data(), priv->size())) {
return -1;
}
if (secp256k1_ec_seckey_verify(ctx, priv->data())) {
break;
}
}
return 0;
}
int ec_get_publickey(const ec_privkey_t *priv, ec_pubkey_t* pub) {
size_t len;
secp256k1_pubkey ec_pub;
if (secp256k1_ec_pubkey_create(ctx, &ec_pub, priv->data()) < 0) {
return -1;
}
len = EC_PUBKEY_SIZE;
secp256k1_ec_pubkey_serialize(ctx, pub->data(), &len, &ec_pub, SECP256K1_EC_COMPRESSED);
return len != EC_PUBKEY_SIZE ? -1 : 0;
}
int ec_generate_key(struct ec_keypair *pair) {
if (ec_generate_privkey(&pair->secret) < 0) {
return -1;
}
return ec_get_publickey(&pair->secret, &pair->pub);
}
} // namespace libantelope

118
src/libsecp256k1/ecdsa.cpp Normal file
View file

@ -0,0 +1,118 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <secp256k1.h>
#include <secp256k1_recovery.h>
#include <libantelope/ec.hpp>
namespace libantelope {
extern secp256k1_context* ctx;
int is_canonical(const unsigned char *d) {
return !(d[1] & 0x80)
&& !(d[1] == 0 && !(d[2] & 0x80))
&& !(d[33] & 0x80)
&& !(d[33] == 0 && !(d[34] & 0x80));
}
static int extended_nonce_function( unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, const unsigned char* algo16,
void* data, unsigned int attempt ) {
(void)attempt; // "use" the variable here to shutup compiler about unsed variable.
return secp256k1_nonce_function_rfc6979(nonce32, msg32, key32, algo16, nullptr, *(unsigned int*) data);
}
int ecdsa_sign(const ec_privkey_t& key, const sha256_t* digest, ec_signature_t& sig) {
for (unsigned int counter = 1; counter < 25; counter++) {
int v = 0;
secp256k1_ecdsa_recoverable_signature s;
if (!secp256k1_ecdsa_sign_recoverable(ctx, &s, (const unsigned char*) digest, key.data(), extended_nonce_function, &counter)) {
return -1;
}
secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig.data() + 1, &v, &s);
if (is_canonical(sig.data())) {
sig[0] = (unsigned char) (27 + 4 + v);
return 0;
}
}
return -1;
}
int ecdsa_verify(const sha256_t* digest, const ec_signature_t& sig, const ec_pubkey_t& key) {
secp256k1_ecdsa_signature ec_sig;
secp256k1_ecdsa_recoverable_signature ec_rec_sig;
secp256k1_pubkey pubkey;
int recid;
recid = sig.at(0) - 27 - 4;
// Parse signature
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &ec_rec_sig, sig.data() + 1, recid)) {
return -1;
}
// Parse public key
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, key.data(), key.size())) {
return -1;
}
// Verify
secp256k1_ecdsa_recoverable_signature_convert(ctx, &ec_sig, &ec_rec_sig);
return secp256k1_ecdsa_verify(ctx, &ec_sig, (const unsigned char*) digest, &pubkey) > 0 ? 0 : -1;
}
int ecdsa_recover(const sha256_t* digest, const ec_signature_t& sig, ec_pubkey_t& pubkey) {
secp256k1_pubkey ec_pubkey;
secp256k1_ecdsa_recoverable_signature ec_sig;
size_t len = EC_PUBKEY_SIZE;
int recid;
recid = sig.at(0) - 27 - 4;
// Parse signature
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &ec_sig, sig.data() + 1, recid)) {
std::cout << "parse sig" << std::endl;
return -1;
}
// Recover public key
if (!secp256k1_ecdsa_recover(ctx, &ec_pubkey, &ec_sig, (const unsigned char*) digest)) {
return -1;
}
secp256k1_ec_pubkey_serialize(ctx, pubkey.data(), &len, &ec_pubkey, SECP256K1_EC_COMPRESSED);
return len != EC_PUBKEY_SIZE ? -1 : 0;
}
} // namespace libantelope

77
src/libsecp256k1/rng.h Normal file
View file

@ -0,0 +1,77 @@
/*************************************************************************
* Copyright (c) 2020-2021 Elichai Turkel *
* Distributed under the CC0 software license, see the accompanying file *
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
*************************************************************************/
#ifndef LIBANTELOPE_LIBSECP256K1_RNG_H
#define LIBANTELOPE_LIBSECP256K1_RNG_H
/*
* This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
* It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
*
* Platform randomness sources:
* Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
* macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
* FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
* OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
* Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
*/
#if defined(_WIN32)
/* Windows throws a bunch of "redefinition" warnings for these headers.
So we disable them temporarily */
#pragma warning( push )
#pragma warning( disable: 4005 )
#include <windows.h>
#include <ntstatus.h>
#pragma warning( pop )
#include <bcrypt.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
#include <sys/random.h>
#elif defined(__OpenBSD__)
#include <unistd.h>
#else
#error "Couldn't identify the OS"
#endif
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
static int fill_random(unsigned char* data, size_t size) {
#if defined(_WIN32)
/* Disable C4267 Warning (dataloss when casting variable to smaller size) temporarily */
#pragma warning( push )
#pragma warning( disable: 4267 )
NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
#pragma warning( pop )
if (res != STATUS_SUCCESS || size > ULONG_MAX) {
return 0;
} else {
return 1;
}
#elif defined(__linux__) || defined(__FreeBSD__)
/* If `getrandom(2)` is not available you should fallback to /dev/urandom */
ssize_t res = getrandom(data, size, 0);
if (res < 0 || (size_t)res != size ) {
return 0;
} else {
return 1;
}
#elif defined(__APPLE__) || defined(__OpenBSD__)
/* If `getentropy(2)` is not available you should fallback to either
* `SecRandomCopyBytes` or /dev/urandom */
int res = getentropy(data, size);
if (res == 0) {
return 1;
} else {
return 0;
}
#endif
return 0;
}
#endif /* LIBANTELOPE_LIBSECP256K1_RNG_H */

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -24,17 +24,16 @@
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/hmac.h>
#include <libeosio/ec.hpp>
#include <libantelope/ec.hpp>
#include "internal.h"
namespace libeosio {
namespace libantelope {
int ec_generate_key(struct ec_keypair *pair) {
BN_CTX *ctx = NULL;
EC_KEY *k = NULL;
int ret = -1;
EC_KEY *k;
BN_CTX *ctx;
int ec_init() {
// Create BIGNUM context.
ctx = BN_CTX_new();
if (ctx == NULL) {
return -1;
@ -43,12 +42,72 @@ int ec_generate_key(struct ec_keypair *pair) {
// Construct curve.
k = EC_KEY_new_by_curve_name(NID_secp256k1);
if (k == NULL) {
goto fail1;
BN_CTX_free(ctx);
return -1;
}
return 0;
}
void ec_shutdown() {
if (ctx) {
BN_CTX_free(ctx);
ctx = NULL;
}
if (k) {
EC_KEY_free(k);
k = NULL;
}
}
int ec_generate_privkey(ec_privkey_t *priv) {
// Generate new private key.
if (EC_KEY_generate_key(k) == 0) {
return -1;
}
if (EC_KEY_priv2oct(k, priv->data(), EC_PRIVKEY_SIZE) == 0) {
return -1;
}
return 0;
}
int ec_get_publickey(const ec_privkey_t *priv, ec_pubkey_t* pub) {
int rc = -1;
const EC_GROUP *group;
EC_POINT *point;
// Load private key
if (EC_KEY_oct2priv(k, priv->data(), EC_PRIVKEY_SIZE) == 0) {
return -1;
}
if ((group = EC_KEY_get0_group(k)) == NULL) {
return -1;
}
if (calculate_pubkey(group, k, &point) == 0) {
return -1;
}
// Encode public key
if (EC_POINT_encode(group, point, pub->data(), EC_PUBKEY_SIZE, ctx) != 0) {
rc = 0;
}
EC_POINT_free(point);
return rc;
}
int ec_generate_key(struct ec_keypair *pair) {
// Generate new key pair.
if (EC_KEY_generate_key(k) != 1) {
goto fail2;
return -1;
}
// Copy private key to binary format.
@ -59,12 +118,7 @@ int ec_generate_key(struct ec_keypair *pair) {
EC_KEY_get0_public_key(k), POINT_CONVERSION_COMPRESSED,
pair->pub.data(), EC_PUBKEY_SIZE, ctx);
ret = 0;
fail2:
EC_KEY_free(k);
fail1:
BN_CTX_free(ctx);
return ret;
return 0;
}
} // namespace libeosio
} // namespace libantelope

185
src/openssl/ecdsa.cpp Normal file
View file

@ -0,0 +1,185 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <libantelope/ec.hpp>
#include "internal.h"
namespace libantelope {
extern BN_CTX *ctx;
int ecdsa_sign(const ec_privkey_t& key, const sha256_t* digest, ec_signature_t& sig) {
int rc = -1;
EC_POINT *pub;
const EC_GROUP *group;
ECDSA_SIG *ecdsa_sig;
EC_KEY *ec_key;
if ((ec_key = EC_KEY_new_secp256k1()) == NULL) {
return -1;
}
if (EC_KEY_oct2priv(ec_key, key.data(), key.size()) < 0) {
goto err1;
}
group = EC_KEY_get0_group(ec_key);
if (group == NULL) {
goto err1;
}
if (calculate_pubkey(group, ec_key, &pub) == 0) {
goto err2;
}
while (1) {
int recid = -1;
const BIGNUM *r, *s;
EC_KEY* tmpk;
ecdsa_sig = ECDSA_do_sign((const unsigned char*) digest, 32, ec_key);
if (ecdsa_sig == NULL) {
goto err2;
}
// Get R and S numbers.
r = ECDSA_SIG_get0_r(ecdsa_sig);
s = ECDSA_SIG_get0_s(ecdsa_sig);
tmpk = EC_KEY_new_by_curve_name( NID_secp256k1 );
for (int i = 0; i < 4; i++) {
if (ECDSA_SIG_recover_key_GFp(tmpk, r, s, (const unsigned char*) digest, 32, i, 1) == 1) {
const EC_POINT *p = EC_KEY_get0_public_key(tmpk);
// Compare public keys
if (EC_POINT_cmp(group, pub, p, ctx) == 0) {
recid = i;
break;
}
}
}
EC_KEY_free( tmpk );
// Could not find recovery id.
if (recid == -1) {
goto err2;
}
if (ECDSA_SIG_serialize(ecdsa_sig, recid, sig.data()) == 0) {
goto out;
}
}
out: rc = 0;
err2:
EC_POINT_free(pub);
err1:
EC_KEY_free(ec_key);
return rc;
}
int ecdsa_verify(const sha256_t* digest, const ec_signature_t& sig, const ec_pubkey_t& pub) {
int recid, ret = -1;
EC_POINT *point;
const EC_GROUP *group;
ECDSA_SIG* ecdsa_sig;
EC_KEY *ec_key;
ec_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
if (ec_key == NULL) {
return -1;
}
if ((ecdsa_sig = ECDSA_SIG_new()) == NULL) {
goto err1;
}
if (ECDSA_SIG_unserialize(sig.data(), ecdsa_sig, &recid) == 0) {
goto err2;
}
if ((group = EC_KEY_get0_group(ec_key)) == NULL) {
goto err2;
}
if ((point = EC_POINT_new(group)) == NULL) {
goto err2;
}
if (EC_POINT_oct2point(group, point, pub.data(), EC_PUBKEY_SIZE, ctx) == 0) {
goto err3;
}
if (EC_KEY_set_public_key(ec_key, point) == 0) {
goto err3;
}
if (ECDSA_do_verify((const unsigned char*) digest, 32, ecdsa_sig, ec_key) == 1) {
ret = 0;
}
err3: EC_POINT_free(point);
err2: ECDSA_SIG_free(ecdsa_sig);
err1: EC_KEY_free(ec_key);
return ret;
}
int ecdsa_recover(const sha256_t* digest, const ec_signature_t& sig, ec_pubkey_t& key) {
int recid;
int ret = -1;
BIGNUM *r, *s;
EC_KEY *ec_key;
// Initialize ec variables.
if ((ec_key = EC_KEY_new_secp256k1()) == NULL) goto err1;
// Unserialize signature into r,s,recid components.
ECDSA_SIG_unserialize_rs(sig.data(), &r, &s, &recid);
// Recover public key.
if (ECDSA_SIG_recover_key_GFp(ec_key, r, s, (const unsigned char*) digest, 32, recid, 1) == 1) {
// Encode point to binary compressed format.
const EC_POINT *p = EC_KEY_get0_public_key(ec_key);
const EC_GROUP *g = EC_KEY_get0_group(ec_key);
if (EC_POINT_encode(g, p, key.data(), EC_PUBKEY_SIZE, ctx) == 0) {
goto err4;
}
ret = 0;
}
err4: BN_free(s);
err3: BN_free(r);
err2: EC_KEY_free(ec_key);
err1: return ret;
}
} // namespace libantelope

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,21 +23,45 @@
*/
#include <openssl/sha.h>
#include <openssl/ripemd.h>
#include <libeosio/hash.hpp>
#include <libantelope/hash.hpp>
namespace libeosio {
namespace libantelope {
int sha256_init(sha256_ctx_t* ctx) {
return SHA256_Init((SHA256_CTX*)ctx);
}
int sha256_update(sha256_ctx_t* ctx, const void *data, std::size_t len) {
return SHA256_Update((SHA256_CTX*)ctx, data, len);
}
int sha256_final(sha256_ctx_t* ctx, sha256_t* out) {
return SHA256_Final((unsigned char*) out, (SHA256_CTX*)ctx);
}
sha256_t* sha256(const unsigned char *data, std::size_t len, sha256_t* out) {
return (sha256_t *) SHA256(data, len, out->data);
return (sha256_t *) SHA256(data, len, (unsigned char*) out);
}
sha256_t* sha256d(const unsigned char *data, std::size_t len, sha256_t* out) {
SHA256(data, len, out->data);
return (sha256_t *) SHA256(out->data, 32, out->data);
SHA256(data, len, (unsigned char*) out);
return (sha256_t *) SHA256((unsigned char*) out, 32, (unsigned char*) out);
}
int ripemd160_init(ripemd160_ctx_t* ctx) {
return RIPEMD160_Init((RIPEMD160_CTX*)ctx);
}
int ripemd160_update(ripemd160_ctx_t* ctx, const void *data, std::size_t len) {
return RIPEMD160_Update((RIPEMD160_CTX*)ctx, data, len);
}
int ripemd160_final(ripemd160_ctx_t* ctx, ripemd160_t* out) {
return RIPEMD160_Final((unsigned char*) out, (RIPEMD160_CTX*)ctx);
}
ripemd160_t* ripemd160(const unsigned char *data, std::size_t len, ripemd160_t* out) {
return (ripemd160_t *) RIPEMD160(data, len, out->data);
return (ripemd160_t *) RIPEMD160(data, len, (unsigned char*) out);
}
} // namespace libeosio
} // namespace libantelope

101
src/openssl/helpers.c Normal file
View file

@ -0,0 +1,101 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <string.h>
// Calcualte a public key from a EC_KEY object.
int calculate_pubkey(const EC_GROUP *group, const EC_KEY *ec_key, EC_POINT **point) {
const BIGNUM* pk;
// Then get the private key number
if ((pk = EC_KEY_get0_private_key(ec_key)) == NULL) {
return 0;
}
// Create a new point.
if ((*point = EC_POINT_new(group)) == NULL) {
return 0;
}
// Multiply curve (group) and private key to get the public key.
return EC_POINT_mul(group, *point, pk, NULL, NULL, NULL);
}
int ECDSA_SIG_unserialize_rs(const unsigned char *sig, BIGNUM **r, BIGNUM **s, int *recid) {
*recid = sig[0] - 27 - 4;
if ((*r = BN_bin2bn(sig + 1, 32, NULL)) == NULL) {
return 0;
}
if ((*s = BN_bin2bn(sig + 33, 32, NULL)) == NULL) {
BN_free(*r);
return 0;
}
return 1;
}
int ECDSA_SIG_unserialize(const unsigned char *sig, ECDSA_SIG *ecdsa_sig, int *recid) {
BIGNUM *r, *s;
if (ECDSA_SIG_unserialize_rs(sig, &r, &s, recid) == 0) {
return 0;
}
if (ECDSA_SIG_set0(ecdsa_sig, r, s) == 0) {
BN_free(r);
BN_free(s);
return 0;
}
// r,s pointers are owned by ECDSA_SIG from this point.
// So we should not free them.
return 1;
}
int ECDSA_SIG_serialize(const ECDSA_SIG *ecdsa_sig, int recid, unsigned char* sig) {
unsigned char* der = NULL;
int bytes, ret = -1;
unsigned char lR, lS;
bytes = i2d_ECDSA_SIG( ecdsa_sig, &der );
lR = der[3];
lS = der[5+lR];
if (lR != 32 || lS != 32) goto err;
memcpy(sig + 1, &der[4], 32);
memcpy(sig + 33, &der[6+32], 32);
sig[0] = recid + 27 + 4;
ret = 0;
err:
free(der);
return ret;
}

65
src/openssl/internal.h Normal file
View file

@ -0,0 +1,65 @@
/**
* MIT License
*
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <openssl/ec.h>
#include <openssl/hmac.h>
#ifndef LIBANTELOPE_OPENSSL_INTERNAL_H
#define LIBANTELOPE_OPENSSL_INTERNAL_H
#define EC_KEY_new_secp256k1() (EC_KEY_new_by_curve_name( NID_secp256k1 ))
#define EC_POINT_encode(group, point, buf, len, ctx) \
EC_POINT_point2oct((group), (point), POINT_CONVERSION_COMPRESSED, (buf), (len), (ctx))
#ifdef __cplusplus
extern "C" {
#endif
int calculate_pubkey(const EC_GROUP *group, const EC_KEY *ec_key, EC_POINT **point);
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, const BIGNUM* r, const BIGNUM* s, const unsigned char *msg, int msglen, int recid, int check);
/**
* Signature serialization function.
* sig must be a pointer to a serialized signature and be atleast 65 (32s + 32 + 1) bytes long.
*
* returns -1 if there was an error. zero otherwise.
*/
int ECDSA_SIG_serialize(const ECDSA_SIG *ecdsa_sig, int recid, unsigned char* sig);
/**
* Signature unserialization functions.
* sig must be a pointer to a serialized signature and be atleast 65 (32s + 32 + 1) bytes long.
*
* returns -1 if there was an error. zero otherwise.
*/
int ECDSA_SIG_unserialize(const unsigned char *sig, ECDSA_SIG *ecdsa_sig, int *recid);
int ECDSA_SIG_unserialize_rs(const unsigned char *sig, BIGNUM **r, BIGNUM **s, int *recid);
#ifdef __cplusplus
}
#endif
#endif /* LIBANTELOPE_OPENSSL_INTERNAL_H */

79
src/openssl/recovery.c Normal file
View file

@ -0,0 +1,79 @@
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// Taken from https://github.com/bitcoin/bitcoin/blob/9b1200c23bbced3a78b58067c1f6414103653795/src/key.cpp#L56
#include <openssl/ec.h>
#include <openssl/bn.h>
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, const BIGNUM* r, const BIGNUM* s, const unsigned char *msg, int msglen, int recid, int check)
{
if (!eckey) return 0;
int ret = 0;
BN_CTX *ctx = NULL;
BIGNUM *x = NULL;
BIGNUM *e = NULL;
BIGNUM *order = NULL;
BIGNUM *sor = NULL;
BIGNUM *eor = NULL;
BIGNUM *field = NULL;
EC_POINT *R = NULL;
EC_POINT *O = NULL;
EC_POINT *Q = NULL;
BIGNUM *rr = NULL;
BIGNUM *zero = NULL;
int n = 0;
int i = recid / 2;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
BN_CTX_start(ctx);
order = BN_CTX_get(ctx);
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
x = BN_CTX_get(ctx);
if (!BN_copy(x, order)) { ret=-1; goto err; }
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
if (!BN_add(x, x, r)) { ret=-1; goto err; }
field = BN_CTX_get(ctx);
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
if (check)
{
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
}
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
n = EC_GROUP_get_degree(group);
e = BN_CTX_get(ctx);
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
zero = BN_CTX_get(ctx);
BN_zero(zero);
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
rr = BN_CTX_get(ctx);
if (!BN_mod_inverse(rr, r, order, ctx)) { ret=-1; goto err; }
sor = BN_CTX_get(ctx);
if (!BN_mod_mul(sor, s, rr, order, ctx)) { ret=-1; goto err; }
eor = BN_CTX_get(ctx);
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
ret = 1;
err:
if (ctx) {
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (R != NULL) EC_POINT_free(R);
if (O != NULL) EC_POINT_free(O);
if (Q != NULL) EC_POINT_free(Q);
return ret;
}

84
src/wif/codec.hpp Normal file
View file

@ -0,0 +1,84 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBANTELOPE_CODEC_H
#define LIBANTELOPE_CODEC_H
#include <libantelope/WIF.hpp>
#include <vector>
namespace libantelope { namespace internal {
/**
* Public-key encoders
*/
typedef void (*pub_encoder_t)(const ec_pubkey_t& key, unsigned char *buf);
void pub_encoder_legacy(const ec_pubkey_t& key, unsigned char *buf);
void pub_encoder_k1(const ec_pubkey_t& key, unsigned char *buf);
/**
* Public-key decoders
*/
typedef bool (*pub_decoder_t)(const std::vector<unsigned char>& buf, ec_pubkey_t& key);
bool pub_decoder_legacy(const std::vector<unsigned char>& buf, ec_pubkey_t& key);
bool pub_decoder_k1(const std::vector<unsigned char>& buf, ec_pubkey_t& key);
/**
* Private-key encoders
*/
typedef size_t (*priv_encoder_t)(const ec_privkey_t&, unsigned char *);
size_t priv_encoder_legacy(const ec_privkey_t& priv, unsigned char *buf);
size_t priv_encoder_k1(const ec_privkey_t& priv, unsigned char *buf);
/**
* Private-key decoders
*/
typedef bool (*priv_decoder_t)(const std::vector<unsigned char>&, ec_privkey_t&);
bool priv_decoder_legacy(const std::vector<unsigned char>& buf, ec_privkey_t& priv);
bool priv_decoder_k1(const std::vector<unsigned char>& buf, ec_privkey_t& priv);
/**
* Signature encoders
*/
typedef void (*sig_encoder_t)(const ec_signature_t& sig, unsigned char *buf);
void sig_encoder_k1(const ec_signature_t& sig, unsigned char *buf);
/**
* Signature decoders
*/
typedef bool (*sig_decoder_t)(const std::vector<unsigned char>& buf, ec_signature_t& sig);
bool sig_decoder_k1(const std::vector<unsigned char>& buf, ec_signature_t& sig);
}} // namespace libantelope::internal
#endif /* LIBANTELOPE_CODEC_H */

129
src/wif/k1.cpp Normal file
View file

@ -0,0 +1,129 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <libantelope/checksum.hpp>
#include <libantelope/hash/ripemd160.hpp>
#include "codec.hpp"
namespace libantelope { namespace internal {
// Just to make it "harder" the calculated checksum for a signature (k1) and pub/priv keys in k1/r1 format.
// has a suffix that is not present in the WIF encoded string.
// So this function is a quick hack to calculate it.
void _checksum_suffix(const unsigned char *in, size_t size, checksum_t check) {
ripemd160_ctx_t ctx;
ripemd160_t md;
ripemd160_init(&ctx);
ripemd160_update(&ctx, in, size);
ripemd160_update(&ctx, "K1", 2);
ripemd160_final(&ctx, &md);
std::memcpy(check, md, CHECKSUM_SIZE);
}
void pub_encoder_k1(const ec_pubkey_t& key, unsigned char *buf) {
checksum_t check;
_checksum_suffix(key.data(), EC_PUBKEY_SIZE, check);
memcpy(buf, key.data(), EC_PUBKEY_SIZE);
memcpy(buf + EC_PUBKEY_SIZE, check, CHECKSUM_SIZE);
}
bool pub_decoder_k1(const std::vector<unsigned char>& buf, ec_pubkey_t& key) {
checksum_t check;
_checksum_suffix(buf.data(), EC_PUBKEY_SIZE, check);
if (memcmp(buf.data() + EC_PUBKEY_SIZE, check, CHECKSUM_SIZE)) {
return false;
}
memcpy(key.data(), buf.data(), EC_PUBKEY_SIZE);
return true;
}
size_t priv_encoder_k1(const ec_privkey_t& priv, unsigned char *buf) {
checksum_t check;
_checksum_suffix(priv.data(), EC_PRIVKEY_SIZE, check);
memcpy(buf, priv.data(), priv.size());
memcpy(buf + EC_PRIVKEY_SIZE, check, CHECKSUM_SIZE);
return EC_PRIVKEY_SIZE + CHECKSUM_SIZE;
}
bool priv_decoder_k1(const std::vector<unsigned char>& buf, ec_privkey_t& priv) {
if (buf.size() != EC_PRIVKEY_SIZE + CHECKSUM_SIZE) {
return false;
}
checksum_t check;
_checksum_suffix(buf.data(), EC_PRIVKEY_SIZE, check);
if (memcmp(buf.data() + EC_PRIVKEY_SIZE, check, CHECKSUM_SIZE)) {
return false;
}
memcpy(priv.data(), buf.data(), priv.size());
return true;
}
void sig_encoder_k1(const ec_signature_t& sig, unsigned char *buf) {
checksum_t check;
_checksum_suffix(sig.data(), EC_SIGNATURE_SIZE, check);
memcpy(buf, sig.data(), sig.size());
memcpy(buf + EC_SIGNATURE_SIZE, check, CHECKSUM_SIZE);
}
bool sig_decoder_k1(const std::vector<unsigned char>& buf, ec_signature_t& sig) {
checksum_t check;
if (buf.size() != EC_SIGNATURE_SIZE + CHECKSUM_SIZE) {
return false;
}
// Calculate checksum
_checksum_suffix(buf.data(), EC_SIGNATURE_SIZE, check);
// And validate
if (memcmp(buf.data() + EC_SIGNATURE_SIZE, check, CHECKSUM_SIZE)) {
return false;
}
// Copy data to output
memcpy(sig.data(), buf.data(), sig.size());
return true;
}
}} // namespace libantelope::internal

80
src/wif/legacy.cpp Normal file
View file

@ -0,0 +1,80 @@
/**
* MIT License
*
* Copyright (c) 2019-2021 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <libantelope/checksum.hpp>
#include "codec.hpp"
namespace libantelope { namespace internal {
#define PRIV_KEY_PREFIX 0x80 /* 0x80 for "Bitcoin mainnet". Always used by EOS. */
void pub_encoder_legacy(const ec_pubkey_t& key, unsigned char *buf) {
checksum_t check;
checksum_ripemd160(key.data(), EC_PUBKEY_SIZE, check);
memcpy(buf, key.data(), EC_PUBKEY_SIZE);
memcpy(buf + EC_PUBKEY_SIZE, check, CHECKSUM_SIZE);
}
bool pub_decoder_legacy(const std::vector<unsigned char>& buf, ec_pubkey_t& key) {
if (!checksum_validate<checksum_ripemd160>(buf.data(), buf.size())) {
return false;
}
memcpy(key.data(), buf.data(), EC_PUBKEY_SIZE);
return true;
}
size_t priv_encoder_legacy(const ec_privkey_t& priv, unsigned char *buf) {
checksum_t check;
buf[0] = PRIV_KEY_PREFIX;
memcpy(buf + 1, priv.data(), EC_PRIVKEY_SIZE);
checksum_sha256d(buf, 1 + EC_PRIVKEY_SIZE, check);
memcpy(buf + 1 + EC_PRIVKEY_SIZE, check, CHECKSUM_SIZE);
return 1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE;
}
bool priv_decoder_legacy(const std::vector<unsigned char>& buf, ec_privkey_t& priv) {
if (buf[0] != PRIV_KEY_PREFIX) {
return false;
}
if (buf.size() != 1 + EC_PRIVKEY_SIZE + CHECKSUM_SIZE) {
return false;
}
if (!checksum_validate<checksum_sha256d>(buf.data(), buf.size())) {
return false;
}
memcpy(priv.data(), buf.data() + 1, priv.size());
return true;
}
}} // namespace libantelope::internal

39
tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,39 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/doctest.cmake)
set(TEST_SRC
main.cpp
# hash
hash/sha256.cpp
hash/ripemd160.cpp
# ec
ec/generate.cpp
ec/pubkey.cpp
ec/ecdsa_sign.cpp
ec/ecdsa_recover.cpp
ec/ecdsa_verify.cpp
# Base58
base58/encode.cpp
base58/decode.cpp
base58/is_base58.cpp
# WIF
WIF/priv_encode.cpp
WIF/priv_decode.cpp
WIF/pub_encode.cpp
WIF/pub_decode.cpp
WIF/sig_encode.cpp
WIF/sig_decode.cpp)
add_executable(doctest ${TEST_SRC})
target_link_libraries(doctest PRIVATE ${LIB_NAME})
target_include_directories(doctest PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include)
doctest_discover_tests(doctest)
if (WITH_BENCHMARK)
add_subdirectory( benchmark )
endif (WITH_BENCHMARK)

59
tests/WIF/priv_decode.cpp Normal file
View file

@ -0,0 +1,59 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_priv_decode [legacy]") {
struct testcase {
std::string name;
std::string key;
libantelope::ec_privkey_t expected;
};
std::vector<struct testcase> tests {
{ "one", "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ", { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D, 0x72, 0xAA, 0x1D } },
{ "two", "5K2hm8apqz281ANDQdtVzifpxcXFTqG5E7Fc6Q5V2ssqPRQ3urJ", { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C } },
{ "three", "5JVanYq9HPvuKgr2FjATYB9NvTsJ4a3CAj5oPYKbr1Ja5MRLsZX", { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C } }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
libantelope::ec_privkey_t result;
CHECK( libantelope::wif_priv_decode(result, it->key) );
CHECK( result == it->expected );
}
}
}
TEST_CASE("WIF::wif_priv_decode [K1]") {
struct testcase {
std::string name;
std::string key;
libantelope::ec_privkey_t expected;
};
std::vector<struct testcase> tests {
{ "one", "PVT_K1_6Mcb23muAxyXaSMhmB6B1mqkvLdWhtuFZmnZsxDczHRvQdp32", { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D, 0x72, 0xAA, 0x1D } },
{ "two", "PVT_K1_2DRBT8jmXT8k9ywNSSbufvhk1hLFhPzWJBpsE2jo12CDoFhcc1", { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C } },
{ "three", "PVT_K1_gJCsP4CwMv4gTkDXiZT8QFhs3NrSB7Sv22ANGrc8Svun9uC9C", { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C } }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
libantelope::ec_privkey_t result;
CHECK( libantelope::wif_priv_decode(result, it->key) );
CHECK( result == it->expected );
}
}
}

51
tests/WIF/priv_encode.cpp Normal file
View file

@ -0,0 +1,51 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_priv_encode [Legacy]") {
struct testcase {
std::string name;
const std::string prefix;
libantelope::ec_privkey_t key;
std::string expected;
};
std::vector<struct testcase> tests {
{ "one", libantelope::WIF_PVT_LEG, { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D,0x72,0xAA,0x1D}, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" },
{ "two", libantelope::WIF_PVT_LEG, { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C}, "5K2hm8apqz281ANDQdtVzifpxcXFTqG5E7Fc6Q5V2ssqPRQ3urJ" },
{ "three", libantelope::WIF_PVT_LEG, { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C}, "5JVanYq9HPvuKgr2FjATYB9NvTsJ4a3CAj5oPYKbr1Ja5MRLsZX" }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
CHECK( libantelope::wif_priv_encode(it->key, it->prefix) == it->expected );
}
}
}
TEST_CASE("WIF::wif_priv_encode [K1]") {
struct testcase {
std::string name;
const std::string prefix;
libantelope::ec_privkey_t key;
std::string expected;
};
std::vector<struct testcase> tests {
{ "one", libantelope::WIF_PVT_K1, { 0x0C, 0x28, 0xFC, 0xA3, 0x86, 0xC7, 0xA2, 0x27, 0x60, 0x0B, 0x2F, 0xE5, 0x0B, 0x7C, 0xAE, 0x11, 0xEC, 0x86, 0xD3, 0xBF, 0x1F, 0xBE, 0x47, 0x1B, 0xE8, 0x98, 0x27, 0xE1, 0x9D,0x72,0xAA,0x1D}, "PVT_K1_6Mcb23muAxyXaSMhmB6B1mqkvLdWhtuFZmnZsxDczHRvQdp32" },
{ "two", libantelope::WIF_PVT_K1, { 0x9F, 0xE3, 0xE3, 0x2B, 0x3C, 0x4B, 0x6B, 0x91, 0x6E, 0x20, 0x6C, 0xB0, 0x91, 0xDF, 0x1F, 0x5E, 0x34, 0x32, 0x88, 0x0B, 0x41, 0x33, 0x86, 0xBD, 0xF2, 0x92, 0xFF, 0x23, 0x06, 0x43, 0xF2, 0x8C}, "PVT_K1_2DRBT8jmXT8k9ywNSSbufvhk1hLFhPzWJBpsE2jo12CDoFhcc1" },
{ "three", libantelope::WIF_PVT_K1, { 0x59, 0x3A, 0x51, 0xB5, 0x5D, 0x56, 0xAA, 0xF0, 0x5B, 0xD9, 0xD1, 0x0E, 0x6B, 0x88, 0x6D, 0xF9, 0xC4, 0x37, 0x09, 0xB2, 0x4C, 0xEC, 0xBB, 0x63, 0x68, 0x92, 0xC2, 0x94, 0x31, 0x48, 0x71, 0x8C}, "PVT_K1_gJCsP4CwMv4gTkDXiZT8QFhs3NrSB7Sv22ANGrc8Svun9uC9C" }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
CHECK( libantelope::wif_priv_encode(it->key, it->prefix) == it->expected );
}
}
}

61
tests/WIF/pub_decode.cpp Normal file
View file

@ -0,0 +1,61 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_pub_decode [legacy]") {
struct testcase {
const char* name;
std::string key;
libantelope::ec_pubkey_t expected;
bool expectedRet;
};
std::vector<struct testcase> tests {
{ "one", "EOS7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq8VeFKy", { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, true },
{ "two", "EOS5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DZfPNeA", { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, true },
{ "three", "EOS8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wm8BJF", { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, true },
{ "wrong_checksum", "EOS8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wm8EgC", { 0x0 }, false },
{ "wrong_length", "EOS7kzJ5iFBmQWWT1LiWgAiocESD7TT", { 0x0 }, false },
{ "not_base58", "EOS7IIIIIOOOO", { 0x0 }, false }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ec_pubkey_t result = { 0x0 };
CHECK( libantelope::wif_pub_decode(result, it->key) == it->expectedRet );
CHECK( result == it->expected );
}
}
}
TEST_CASE("WIF::wif_pub_decode [k1]") {
struct testcase {
const char* name;
std::string key;
libantelope::ec_pubkey_t expected;
bool expectedRet;
};
std::vector<struct testcase> tests {
{ "one", "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq7AxzWu", { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, true },
{ "two", "PUB_K1_5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DViSUUa", { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, true },
{ "three", "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXpw", { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, true },
{ "wrong_checksum", "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXgE", { 0x0 }, false },
{ "wrong_length", "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TT", { 0x0 }, false },
{ "not_base58", "PUB_K1_7IIIIIOOOO", { 0x0 }, false }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ec_pubkey_t result = { 0x0 };
CHECK( libantelope::wif_pub_decode(result, it->key) == it->expectedRet );
CHECK( result == it->expected );
}
}
}

71
tests/WIF/pub_encode.cpp Normal file
View file

@ -0,0 +1,71 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_pub_encode [legacy]") {
struct testcase {
std::string name;
const std::string prefix;
libantelope::ec_pubkey_t key;
std::string expected;
};
std::vector<struct testcase> tests {
{ "one", libantelope::WIF_PUB_LEG, { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, "EOS7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq8VeFKy" },
{ "two", libantelope::WIF_PUB_LEG, { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, "EOS5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DZfPNeA" },
{ "three", libantelope::WIF_PUB_LEG, { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, "EOS8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wm8BJF" }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
CHECK( libantelope::wif_pub_encode(it->key, it->prefix) == it->expected );
}
}
}
TEST_CASE("WIF::wif_pub_encode [custom prefix]") {
struct testcase {
std::string name;
libantelope::ec_pubkey_t key;
std::string expected;
};
std::vector<struct testcase> tests {
{ "one", { 0x03, 0xfc, 0xcc, 0xcb, 0x20, 0x5a, 0x04, 0xfe, 0x34, 0x25, 0x1f, 0x1e, 0x75, 0x82, 0x85, 0x13, 0xed, 0xda, 0x6f, 0x33, 0xba, 0xf4, 0xeb, 0xc7, 0x4d, 0xc4, 0xa6, 0xe8, 0x91, 0x4a, 0xf6, 0x81, 0x43 }, "ZYX8kZxzEW6Loj9Nc3SUcHdS1TMH1zfu4JFu239Af1W3dXrA6GfHG" },
{ "two", { 0x02, 0x8c, 0x58, 0xcb, 0xd4, 0xa4, 0xef, 0x23, 0xc3, 0xb8, 0x0e, 0x8f, 0xff, 0xe3, 0x4e, 0xe0, 0x1b, 0x64, 0x98, 0xe2, 0x12, 0xc9, 0xda, 0xbd, 0xee, 0x70, 0x7c, 0xf6, 0x6b, 0x2a, 0x15, 0x77, 0x06 }, "ZYX5xJKrJnDZQMzRZEzpm34g3MZxa93sANF5G1m9SnKH5gk7BCmAE" },
{ "three", { 0x03, 0xa2, 0xea, 0xad, 0x09, 0x3e, 0x34, 0x31, 0x04, 0xa3, 0x7a, 0xc0, 0x10, 0x29, 0x5d, 0xce, 0x41, 0xb3, 0x60, 0x3a, 0x2d, 0x21, 0xd9, 0x44, 0x0f, 0x7b, 0x4c, 0xad, 0xad, 0x7e, 0x4f, 0x73, 0x78 }, "ZYX84z22iUYZ4tUfNDh8WNvL9wtfMn1mDtYpdezp2v73qcpLTVesz" }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
CHECK( libantelope::wif_pub_encode(it->key, "ZYX") == it->expected );
}
}
}
TEST_CASE("WIF::wif_pub_encode [k1]") {
struct testcase {
std::string name;
const std::string prefix;
libantelope::ec_pubkey_t key;
std::string expected;
};
std::vector<struct testcase> tests {
{ "one", libantelope::WIF_PUB_K1, { 0x03, 0x7a, 0x0e, 0x6b, 0xfd, 0xe4, 0xf1, 0xad, 0x36, 0x3f, 0x3a, 0xf9, 0xe0, 0x93, 0x63, 0x5a, 0xa9, 0x99, 0x21, 0x15, 0xbc, 0x23, 0x35, 0x75, 0x13, 0x69, 0x55, 0xee, 0x3f, 0xf8, 0xfd, 0x97, 0xec }, "PUB_K1_7kzJ5iFBmQWWT1LiWgAiocESD7TTNuuPCdYREUQysruq7AxzWu" },
{ "two", libantelope::WIF_PUB_K1, { 0x02, 0x5e, 0x94, 0xa5, 0xe7, 0x9f, 0x66, 0x37, 0x55, 0x7e, 0xc2, 0x28, 0x30, 0x40, 0x82, 0x9a, 0x38, 0x72, 0x10, 0x96, 0x6e, 0x15, 0xb7, 0xa5, 0x8a, 0x27, 0x9a, 0x71, 0x06, 0xa7, 0x64, 0x23, 0x30 }, "PUB_K1_5c9HkNCJLDebe2Wvapp8bpB38Pf1QWNpkrsFy3mshg7DViSUUa" },
{ "three", libantelope::WIF_PUB_K1, { 0x03, 0xd4, 0xc6, 0x2a, 0xdc, 0x11, 0x1c, 0x65, 0x7a, 0x9f, 0x5b, 0xba, 0x96, 0x3f, 0xbb, 0x2a, 0x69, 0x2e, 0xc5, 0x4a, 0x48, 0x3b, 0xa3, 0x5f, 0x2a, 0x37, 0x6c, 0x59, 0x95, 0xb1, 0x95, 0x1c, 0xc9 }, "PUB_K1_8SwZMY8DChbbmRKS3wdHCAbv1VWgTRmQEDSaLyJk8pG4wKBXpw" }
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
CHECK( libantelope::wif_pub_encode(it->key, it->prefix) == it->expected );
}
}
}

100
tests/WIF/sig_decode.cpp Normal file
View file

@ -0,0 +1,100 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_sig_decode") {
struct testcase {
const char *name;
std::string input;
libantelope::ec_signature_t expected;
bool expectedRet;
};
std::vector<testcase> tests = {
{
"valid #1",
"SIG_K1_KYq4LKCQ1Pdk38TY4FqwxiHRQd53b2kffB7G2Lt5WiV8VzZAvwCdbRVC5AjZvEkmXSEwyFkAFACHj1hYos8hB7Ass7RY2f",
{
0x20,0x1f,0x32,0xfb,0x5f,0x24,0xd2,0x57,
0x5c,0xcc,0x51,0xf3,0xf1,0x60,0x47,0xf7,
0x5c,0x5e,0x8e,0xb0,0xb1,0xc2,0x6d,0x76,
0x07,0xc1,0x9e,0x24,0xd7,0xbb,0xc1,0x69,
0x9a,0x04,0xba,0xa7,0x32,0xc7,0xef,0x83,
0x1d,0xa9,0x40,0xde,0x9c,0xc8,0xf1,0xd9,
0x7b,0xe5,0x0e,0xaf,0x90,0xdf,0xce,0x98,
0xc5,0x34,0x55,0x04,0x9b,0x20,0x72,0x9a,
0x96
},
true
},
{
"valid #2",
"SIG_K1_K2Liiq4wXeeWfndxGM23xms5AR5oK99RvKRR9NpW9eemKWKD1FmpmnwEbpZUSBzQC77KwYptvW6cwGjWR6D3qDddH3w69J",
{
0x1f,0x36,0x28,0x1c,0xe3,0xda,0x53,0x40,
0x09,0x28,0xa8,0xad,0x68,0xb3,0x3a,0xb7,
0x90,0xf7,0x55,0xff,0x60,0xf0,0x51,0x9b,
0xb6,0xd8,0x48,0xff,0x09,0xbb,0x5d,0x17,
0xa2,0x1a,0xe0,0x55,0xe5,0x75,0xf4,0xb9,
0x67,0x5a,0x42,0x2c,0xf3,0x8f,0x40,0x32,
0x1d,0x76,0x23,0x54,0xae,0xdc,0xfb,0xb9,
0xf3,0x16,0x88,0x3e,0x62,0xec,0x7f,0x0d,
0x9f
},
true
},
{
"valid #3",
"SIG_K1_Jxm4D2csP298MurVbsntqZvU6RrMvLufVGQ1URtjdKQ6tdbkkifW5ptcbhW7oGP9nfJ6rzW7Jqhgu2RsDm9ToDyCmy9yk7",
{
0x1f,0x1a,0xca,0x19,0x60,0x39,0x18,0x63,0x53,0x18,0xea,0x29,0x6e,0x4a,0x16,0x81,0x8f,0xf0,0xdc,0xe,0xad,0x38,0x1e,0x5f,0x0,0xde,0xb1,0xd5,0x1d,0xf5,0xe4,0xfb,0x8e,0x6d,0xdd,0x5d,0x79,0xe2,0x1,0x5d,0xac,0x75,0x72,0xcd,0xe7,0x84,0x47,0x8d,0x49,0x68,0xa0,0x7f,0x31,0x22,0xbb,0x6d,0x49,0x9b,0x43,0x92,0x83,0xec,0xbf,0xf8,0x4
},
true
},
{
"invalid #1 - prefix",
"PREF_KaK2DE1we98JKmQRfEP1TXcySbHAfVqUBCqhZ2VtUo3v4QFyFNPg2YRmsiRHk5ePFxqxhX1Y8VS2NC5DYfhQyTFmouTLBi",
{},
false,
},
{
"invalid #2 - wrong checksum",
"SIG_K1_K2Liiq4wXeeWfndxGM23xms5AR5oK99RvKRR9NpW9eemKWKD1FmpmnwEbpZUSBzQC77KwYptvW6cwGjWR6D3qDddH3w6xx",
{},
false,
},
{
"invalid #3 - to long",
"SIG_K1_uns8EQ4m3QJgVPPNNNc9HFDgvmsXe7yrAB66nsCnNjhH3zhBvzoBLf22GtWDwmpUQWByr7VExHZ8aZnuTMEDkHEaS4dzP8oT2qupW7HsCzeRKfSXMZ48jKnMV4aqiK8VqH66KD9Mn",
{},
false,
},
{
"invalid #4 - to short",
"SIG_K1_8BTSpezp9ywsEwVPhMgMLgDMZV1xHuycq2DNbSrVcSZjriyK2FBrh5p518",
{},
false,
},
{
"invalid #5 - non-base58",
"SIG_K1_6sCX2LiY2EpKdJtK7DJMGUETSNdDBNP3MjoZlF1i2V6RFhjoqd1jZbIAobdeARzQxpqHBvJpOWhKxBdA28CsVYJQe0VdcL",
{},
false,
}
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ec_signature_t result;
CHECK( libantelope::wif_sig_decode(result, it->input) == it->expectedRet );
if (it->expectedRet == true) {
CHECK( result == it->expected );
}
}
}
}

53
tests/WIF/sig_encode.cpp Normal file
View file

@ -0,0 +1,53 @@
#include <libantelope/WIF.hpp>
#include <libantelope/ec.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("WIF::wif_sig_encode") {
struct testcase {
const char *name;
libantelope::ec_signature_t sig;
std::string expected;
};
std::vector<struct testcase> tests = {
{
"first",
{
0x20,0x1f,0x32,0xfb,0x5f,0x24,0xd2,0x57,
0x5c,0xcc,0x51,0xf3,0xf1,0x60,0x47,0xf7,
0x5c,0x5e,0x8e,0xb0,0xb1,0xc2,0x6d,0x76,
0x07,0xc1,0x9e,0x24,0xd7,0xbb,0xc1,0x69,
0x9a,0x04,0xba,0xa7,0x32,0xc7,0xef,0x83,
0x1d,0xa9,0x40,0xde,0x9c,0xc8,0xf1,0xd9,
0x7b,0xe5,0x0e,0xaf,0x90,0xdf,0xce,0x98,
0xc5,0x34,0x55,0x04,0x9b,0x20,0x72,0x9a,
0x96
},
"SIG_K1_KYq4LKCQ1Pdk38TY4FqwxiHRQd53b2kffB7G2Lt5WiV8VzZAvwCdbRVC5AjZvEkmXSEwyFkAFACHj1hYos8hB7Ass7RY2f"
},
{
"second",
{
0x1f,0x36,0x28,0x1c,0xe3,0xda,0x53,0x40,
0x09,0x28,0xa8,0xad,0x68,0xb3,0x3a,0xb7,
0x90,0xf7,0x55,0xff,0x60,0xf0,0x51,0x9b,
0xb6,0xd8,0x48,0xff,0x09,0xbb,0x5d,0x17,
0xa2,0x1a,0xe0,0x55,0xe5,0x75,0xf4,0xb9,
0x67,0x5a,0x42,0x2c,0xf3,0x8f,0x40,0x32,
0x1d,0x76,0x23,0x54,0xae,0xdc,0xfb,0xb9,
0xf3,0x16,0x88,0x3e,0x62,0xec,0x7f,0x0d,
0x9f
},
"SIG_K1_K2Liiq4wXeeWfndxGM23xms5AR5oK99RvKRR9NpW9eemKWKD1FmpmnwEbpZUSBzQC77KwYptvW6cwGjWR6D3qDddH3w69J"
},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
CHECK( libantelope::wif_sig_encode(it->sig) == it->expected );
}
}
}

48
tests/base58/decode.cpp Normal file
View file

@ -0,0 +1,48 @@
#include <libantelope/base58.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("base58_decode") {
struct testcase {
const char* name;
std::string in;
std::string expectedOut;
bool expectedReturn;
};
std::vector<testcase> tests = {
{"empty", "","", true},
{"invalid","OI","",false},
{
"valid #1",
"2nPTv2DT874jRaYBN4uhM9mT2iRiwdJuCXuX5buUHyyvWUSu6cX62i8HYo8PsWqgs9DHbwhpSpV5SVUnCqyLcpxcuGanH68eXgzZTGq",
"Quisque ut ipsum lorem. Nullam ac justo elit. Sed gravida convallis mattis.",
true
},
{
"valid #2",
"5yAgp6rBagDHQZ3GacZSeaEPF2jfuwVHM21aNfXETJgn3EkArxc5UWSq1RM",
"Cras fringilla, eros et imperdiet tincidunt",
true
},
{
"valid #3",
"9P7SxYWTWMq5hHkri53b1CGvWKRXxq3uXWPs5RiVtYagFrsnTXDxvKnk1twkPmV7BuxcRhBHWSwFLXpXbmdfHwZrnDaTB3wrBhsjm2Dd7F95ixh5vQLxajmT8hd22yUbvXuAZci8vTgFWMUyQi5YzWwntQiK5KFDkx3oA7kxvdU5t1yJZur84a9aKTCihEWtvCJ6LoBCpxvyB16YaCKeBQWLbUqoaXvFoDM78BpKD8biYyWQhnzHonjdwAS4KNXs5ByBdBvvPK1Q2Knr8zuFZxKHEFmgZGFTt8SMSsTDjkanUjojbfpJt5gcrHh6UFrt45n7kT9sj9Xsf1UyXZG3E2H85jXSbVnKowz2VPq1TkLLUKG8CSfdH3fVRp2E3yL5cpbbFWngbMzsbBZDgr4kPPcazebvSZ8qm8taBcBmt1ry25ey9TfFbMzP4FR1q9yjvkqGusMtrrBFm8YEeRmoMugMQoXvUgpExh29j",
"Praesent massa nibh, feugiat ac aliquet sed, varius quis metus. Fusce auctor imperdiet purus. Vivamus elementum risus vel imperdiet condimentum. Nunc iaculis, sem eu sollicitudin tempus, nibh felis scelerisque orci, a tincidunt felis lectus in nulla. Vestibulum egestas eu elit id luctus. Vivamus eget ipsum neque. Fusce eleifend mauris a tempus vehicula.",
true
},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
std::vector<unsigned char> result;
std::vector<unsigned char> expectedOut(it->expectedOut.begin(), it->expectedOut.end());
CHECK( libantelope::base58_decode(it->in, result) == it->expectedReturn );
CHECK( result == expectedOut );
}
}
}

34
tests/base58/encode.cpp Normal file
View file

@ -0,0 +1,34 @@
#include <libantelope/base58.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("base58::base58_encode") {
struct testcase {
const char* name;
std::string in;
std::string expected;
};
std::vector<struct testcase> tests = {
{"empty","",""},
{
"first",
"Quisque ut ipsum lorem. Nullam ac justo elit. Sed gravida convallis mattis.",
"2nPTv2DT874jRaYBN4uhM9mT2iRiwdJuCXuX5buUHyyvWUSu6cX62i8HYo8PsWqgs9DHbwhpSpV5SVUnCqyLcpxcuGanH68eXgzZTGq"
},
{
"second",
"Cras fringilla, eros et imperdiet tincidunt",
"5yAgp6rBagDHQZ3GacZSeaEPF2jfuwVHM21aNfXETJgn3EkArxc5UWSq1RM"
},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
CHECK( libantelope::base58_encode(it->in) == it->expected );
}
}
}

View file

@ -0,0 +1,52 @@
#include <libantelope/base58.hpp>
#include <iostream>
#include <vector>
#include <doctest.h>
TEST_CASE("base58::is_base58 [string]") {
struct testcase{
const char *name;
std::string input;
size_t expected;
};
std::vector<struct testcase> tests = {
{"empty", "", std::string::npos},
{"zero", "2SdasxuGGdVU5VVyrXiko4jKASeS57E0P9uokzUphZt7tZxt24bzsEwvre", 31},
{"O", "2RTAsaYN2fpxVEDzaQht8ZnAUmwRpJz9C18VXrAWypxQSijRb9295kw13MA8krpRzK5cj2N5p84qQh3OGJrucW8hkLNy3aaEd2rTVhYkekhFiQoQ41JiNScD5KjmpDDxy", 79},
{"I", "5hWrCBA55zLmKpIhZd3RS1DHsJ7SnZpnyBfmibqGpDCJ7QCJGkogvhqPvGuwMgwNHzuZFyR", 14},
{"l", "lHxVA2fQKawLAK9MCJSr2xaWyDpoquQxVP6MMchdhzY49TjTfti8LDR6YL", 0},
{"all_valid", "2BCoJ2BqNWorSoQcSWCQNanB8teoKFaqjojWGEXPBCPPdoGyVN8dgmKRdw", std::string::npos},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
CHECK(libantelope::is_base58(it->input) == it->expected);
}
}
}
TEST_CASE("base58::is_base58 [char]") {
std::string valid_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
std::string invalid_alphabet = "0OIl";
SUBCASE("valid") {
for(std::size_t i = 0; i < valid_alphabet.length(); i++) {
char ch = valid_alphabet[i];
CHECK(libantelope::is_base58(ch));
}
}
SUBCASE("invalid") {
for(std::size_t i = 0; i < invalid_alphabet.length(); i++) {
char ch = invalid_alphabet[i];
CHECK_FALSE(libantelope::is_base58(ch));
}
}
}

View file

@ -0,0 +1,3 @@
add_executable(bench_ec ec.cpp)
target_link_libraries(bench_ec PRIVATE ${LIB_NAME})

View file

@ -1,7 +1,7 @@
/**
* MIT License
*
* Copyright (c) 2019-2020 EOS Sw/eden
* Copyright (c) 2019-2023 EOS Sw/eden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,35 +21,40 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef LIBEOSIO_TYPES_H
#define LIBEOSIO_TYPES_H
#include <chrono>
#include <libantelope/ec.hpp>
#include <libantelope/WIF.hpp>
#include <array>
namespace libeosio {
std::chrono::duration<float> _run(size_t num_keys) {
auto start = std::chrono::steady_clock::now();
for(size_t i = 0; i < num_keys; i++) {
struct libantelope::ec_keypair k;
libantelope::ec_generate_key(&k);
}
return std::chrono::steady_clock::now() - start;
}
#define EC_PRIVKEY_SIZE 32
void test(size_t num_keys) {
float t, kps;
/*
* Compressed format!
* z||x, where byte z specifies which (of the 2) solutions of the quadratic equation y is.
* Each cordinate is 32 bytes.
*/
#define EC_PUBKEY_SIZE (32 + 1)
typedef std::array<unsigned char, EC_PRIVKEY_SIZE> ec_privkey_t;
typedef std::array<unsigned char, EC_PUBKEY_SIZE> ec_pubkey_t;
std::cout << "Running benchmark for " << num_keys << " keys" << std::endl;
t = _run(num_keys).count();
kps = static_cast<float>(num_keys) / t;
struct ec_keypair {
ec_privkey_t secret;
ec_pubkey_t pub;
};
std::cout << "Time: " << t << std::endl
<< "KPS: " << kps << std::endl;
}
// Hashes.
int main() {
libantelope::ec_init();
typedef struct { unsigned char data[20]; } ripemd160_t;
typedef struct { unsigned char data[32]; } sha256_t;
test(1000);
test(10000);
test(100000);
} // namespace libeosio
libantelope::ec_shutdown();
#endif /* LIBEOSIO_TYPES_H */
return 0;
}

189
tests/cmake/doctest.cmake Normal file
View file

@ -0,0 +1,189 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
doctest
-----
This module defines a function to help use the doctest test framework.
The :command:`doctest_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each doctest test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the doctest name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: doctest_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
doctest_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[ADD_LABELS value]
[TEST_LIST var]
[JUNIT_OUTPUT_DIR dir]
)
``doctest_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-cases`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the doctest executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the doctest executable with the ``--list-test-cases`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``doctest_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``doctest_discover_tests``.
``ADD_LABELS value``
Specifies if the test labels should be set automatically.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``doctest_discover_tests()``.
Note that this variable is only available in CTest.
``JUNIT_OUTPUT_DIR dir``
If specified, the parameter is passed along with ``--reporters=junit``
and ``--out=`` to the test executable. The actual file name is the same
as the test target, including prefix and suffix. This should be used
instead of EXTRA_ARGS to avoid race conditions writing the XML result
output when using parallel test execution.
#]=======================================================================]
#------------------------------------------------------------------------------
function(doctest_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;JUNIT_OUTPUT_DIR"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;ADD_LABELS"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_ADD_LABELS=${_ADD_LABELS}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_JUNIT_OUTPUT_DIR=${_JUNIT_OUTPUT_DIR}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_DOCTEST_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT CMAKE_VERSION VERSION_LESS 3.10)
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if(NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_DOCTEST_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/doctestAddTests.cmake
)

View file

@ -0,0 +1,120 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(add_labels ${TEST_ADD_LABELS})
set(junit_output_dir "${TEST_JUNIT_OUTPUT_DIR}")
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
)
endif()
if("${spec}" MATCHES .)
set(spec "--test-case=${spec}")
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-cases
OUTPUT_VARIABLE output
RESULT_VARIABLE result
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Parse output
foreach(line ${output})
if("${line}" STREQUAL "===============================================================================" OR "${line}" MATCHES [==[^\[doctest\] ]==])
continue()
endif()
set(test ${line})
set(labels "")
if(${add_labels})
# get test suite that test belongs to
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --test-case=${test} --list-test-suites
OUTPUT_VARIABLE labeloutput
RESULT_VARIABLE labelresult
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${labelresult} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${labelresult}\n"
" Output: ${labeloutput}\n"
)
endif()
string(REPLACE "\n" ";" labeloutput "${labeloutput}")
foreach(labelline ${labeloutput})
if("${labelline}" STREQUAL "===============================================================================" OR "${labelline}" MATCHES [==[^\[doctest\] ]==])
continue()
endif()
list(APPEND labels ${labelline})
endforeach()
endif()
if(NOT "${junit_output_dir}" STREQUAL "")
# turn testname into a valid filename by replacing all special characters with "-"
string(REGEX REPLACE "[/\\:\"|<>]" "-" test_filename "${test}")
set(TEST_JUNIT_OUTPUT_PARAM "--reporters=junit" "--out=${junit_output_dir}/${prefix}${test_filename}${suffix}.xml")
else()
unset(TEST_JUNIT_OUTPUT_PARAM)
endif()
# use escape commas to handle properly test cases with commas inside the name
string(REPLACE "," "\\," test_name ${test})
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"--test-case=${test_name}"
"${TEST_JUNIT_OUTPUT_PARAM}"
${extra_args}
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
LABELS ${labels}
)
unset(labels)
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

103
tests/ec/ecdsa_recover.cpp Normal file
View file

@ -0,0 +1,103 @@
#include <libantelope/ec.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("ec::ecdsa_recover") {
struct testcase {
const char *name;
libantelope::sha256_t dgst;
libantelope::ec_signature_t sig;
libantelope::ec_pubkey_t expected;
int expectedRet;
};
std::vector<testcase> tests = {
{
"valid #1",
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
// SIG_K1_KdgBih1poWj8DYZXwLxMdjaHMzYhuAVp7XshR9ZjrZSubZwsgSpiyUKXu44NmCtKgRFswmqKaioWLTuGZrXwYPsSNCSyyr
{
0x20, 0x44, 0x3f, 0x72, 0x22, 0xfd, 0x7a, 0x1f, 0x56, 0x2d, 0xef, 0x01, 0x55, 0x40, 0xcf, 0x50, 0x6f, 0x5f, 0xdd, 0xfe, 0x71, 0xd7, 0x18, 0xc9, 0xa8, 0xc8, 0xbe, 0x00, 0x96, 0xf8, 0x7c, 0xc7,
0x1f, 0x2d, 0xd0, 0xd1, 0xfc, 0x4a, 0x22, 0x6a, 0x25, 0xc4, 0x7c, 0x99, 0xf9, 0xd8, 0x30, 0xfa, 0x8b, 0x5c, 0x33, 0x36, 0x61, 0xd7, 0xcf, 0x6d, 0x04, 0x97, 0x61, 0x76, 0x47, 0x65, 0x30, 0x7b,
0x66
},
// Public Key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
0
},
{
"valid #2",
{
0x19, 0xd3, 0xe0, 0x8b, 0xbb, 0xad, 0x5f, 0x02,
0x35, 0xa8, 0xa8, 0xf8, 0x1a, 0x7f, 0xa1, 0xe0,
0xf8, 0x50, 0xdd, 0x39, 0x12, 0xe3, 0xc6, 0x55,
0xb4, 0x35, 0xd4, 0x78, 0x6b, 0x93, 0x64, 0xa6
},
// SIG_K1_K4XXx6oSYBzcwzscMstvSxruxdkTCinyN9dnRo4DuBkCCpQbCJQcJmbE7aAmNueBYCccHyyDK5JDfMpvewRF2rGUFtSE2y
{
0x1f, 0x46, 0xde, 0x7a, 0x7e, 0x87, 0xa7, 0xb0, 0x42, 0xce, 0xdc, 0x57, 0xc9, 0x0d, 0x64, 0x4c, 0xc7, 0x4d, 0xe6, 0x19, 0x5d, 0x34, 0x4e, 0xba, 0xfb, 0xdf, 0x26, 0x79, 0xa1, 0xc6, 0x99, 0x98,
0xa7, 0x1f, 0x65, 0xcd, 0xab, 0x2d, 0x19, 0x75, 0x27, 0xdc, 0xb2, 0xc5, 0x46, 0x87, 0x5d, 0xbe, 0xc5, 0x8d, 0xb2, 0xb8, 0x7f, 0x15, 0x47, 0xd7, 0xc7, 0x94, 0x0a, 0xd5, 0x52, 0xd9, 0xe3, 0x93,
0xd7
},
// Public Key: EOS6tVtKhTpM6yU7kkiRz1AecDJPcBQo2w4x4oytJbJi5PMV2Rcw2
{ 0x03, 0x07, 0x69, 0xbb, 0xa5, 0x2c, 0xd2, 0xe1, 0x3b, 0x3e, 0x0a, 0x40, 0xb3, 0xa2, 0x44, 0xad, 0x71, 0x6e, 0x32, 0x64, 0x9c, 0x3a, 0x64, 0x27, 0x4f, 0x31, 0x86, 0x8a, 0x4c, 0x69, 0x58, 0x86, 0x49 },
0
},
{
"valid #3",
{
0x1b, 0x01, 0x0b, 0xe5, 0xce, 0x6a, 0x49, 0xc7,
0xcd, 0x04, 0x86, 0x0d, 0xef, 0x63, 0x1c, 0x6a,
0xcc, 0xd5, 0x17, 0x47, 0x2e, 0x74, 0x5b, 0xa6,
0xc8, 0xaf, 0x26, 0x1b, 0x15, 0x7e, 0x11, 0xec
},
// SIG_K1_K54CVeQjFREm9Z92jutWESZWb9WQfCRZ2KfMtisfsnxedppeSMxTrZ9fYDLiJTfE79zvLCHb5NysAEcNdh7HiBvtU4Ahhh
{
0x1f, 0x4a, 0xe9, 0x04, 0x20, 0xfa, 0x7b, 0x9d, 0x56, 0xc6, 0x00, 0x5c, 0x83, 0x70, 0xa9, 0x26, 0x41, 0x7d, 0xe8, 0xeb, 0xe7, 0x75, 0xea, 0x6f, 0x75, 0xa7, 0x7c, 0x98, 0x10, 0x27, 0xbf, 0xce,
0x48, 0x41, 0x74, 0xb0, 0xe3, 0xb1, 0x4b, 0x06, 0x2c, 0x53, 0x93, 0xbc, 0x35, 0xea, 0xac, 0xd7, 0x9e, 0x07, 0xa7, 0xa1, 0x2e, 0xac, 0xa0, 0x81, 0x45, 0xdb, 0xd4, 0x53, 0x68, 0xda, 0xaa, 0xc6,
0xfc
},
// Public Key: EOS7Xtaa4y44gYapth4MH5bdtCvdtQvVLdsW7a8thVAuvNAkj8X7i
{ 0x03, 0x5c, 0x50, 0x81, 0xef, 0xa6, 0x46, 0x00, 0x5a, 0xb9, 0xd8, 0x2b, 0xfe, 0xd8, 0xe1, 0x6d, 0x15, 0x42, 0x9e, 0x9a, 0xcb, 0xc9, 0xd6, 0xb3, 0x2e, 0x5a, 0xe3, 0xed, 0xa5, 0x8d, 0x6a, 0x42, 0x6c },
0
},
{
"not valid #1 (non valid signature)",
{
0xde, 0x01, 0x64, 0x03, 0x39, 0x01, 0x66, 0x8b,
0xa0, 0x39, 0xef, 0x31, 0x61, 0xc7, 0xc8, 0x9d,
0x15, 0x4b, 0xc6, 0x7b, 0x99, 0x5c, 0xba, 0x9b,
0x23, 0x8a, 0x76, 0x4b, 0x81, 0xf2, 0xff, 0xeb
},
{
0x1f, 0x4b, 0xe9, 0x04, 0x20, 0xfa, 0x7b, 0x9d, 0x56, 0xc6, 0x00, 0x5c, 0x83, 0x70, 0xa9, 0x26, 0x41, 0x7d, 0xe8, 0xeb, 0xe7, 0x75, 0xea, 0x6f, 0x75, 0xa7, 0x7c, 0x98, 0x10, 0x27, 0xbf, 0xce,
0x48, 0x41, 0x74, 0xb0, 0xe3, 0xb1, 0x4b, 0x06, 0x2c, 0x53, 0x93, 0xbc, 0x35, 0xea, 0xac, 0xd7, 0x9e, 0x07, 0xa7, 0xa1, 0x2e, 0xac, 0xa0, 0x81, 0x45, 0xdb, 0xd4, 0x53, 0x68, 0xda, 0xaa, 0xc6,
0xfe
},
{ },
-1
},
};
libantelope::ec_init();
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ec_pubkey_t result;
CHECK( libantelope::ecdsa_recover(&it->dgst, it->sig, result) == it->expectedRet );
if (it->expectedRet == 0) {
CHECK( result == it->expected );
}
}
}
libantelope::ec_shutdown();
}

115
tests/ec/ecdsa_sign.cpp Normal file
View file

@ -0,0 +1,115 @@
#include <libantelope/ec.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("ec::ecdsa_sign") {
struct testcase {
const char *name;
libantelope::ec_privkey_t key;
libantelope::ec_pubkey_t pub;
libantelope::sha256_t dgst;
};
std::vector<testcase> tests = {
{
"valid #1",
// Private Key: 5Ke4YqL2TCtiUTTA1CVMXSrrEHuK9HzbUSWX791yC2UaX2dWRDw
{ 0xf0, 0x2d, 0x00, 0x72, 0x8a, 0x7a, 0x93, 0x86, 0xaf, 0xbe, 0x19, 0xab, 0x79, 0x8c, 0xa1, 0x61, 0xab, 0x96, 0x74, 0x7f, 0xe5, 0x97, 0x19, 0x07, 0xb1, 0xc8, 0x65, 0x63, 0xc8, 0x11, 0xe6, 0x74 },
// Public key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
},
{
"valid #2",
// Private Key: 5J1VLF3TtdM3FiaUZNjeUXTUmVmRRULSdxrGf3XwQkRAZLcN64b
{ 0x19, 0x6f, 0xd7, 0xa5, 0x3f, 0xab, 0x59, 0x8e, 0xa5, 0xef, 0xec, 0x79, 0xdd, 0xbc, 0x49, 0xae, 0xef, 0xff, 0x41, 0x3c, 0x5f, 0xfe, 0x50, 0x3a, 0x66, 0xbc, 0xff, 0xf1, 0x32, 0x1b, 0x8c, 0x2f },
// Public key: EOS6tVtKhTpM6yU7kkiRz1AecDJPcBQo2w4x4oytJbJi5PMV2Rcw2
{ 0x03, 0x07, 0x69, 0xbb, 0xa5, 0x2c, 0xd2, 0xe1, 0x3b, 0x3e, 0x0a, 0x40, 0xb3, 0xa2, 0x44, 0xad, 0x71, 0x6e, 0x32, 0x64, 0x9c, 0x3a, 0x64, 0x27, 0x4f, 0x31, 0x86, 0x8a, 0x4c, 0x69, 0x58, 0x86, 0x49 },
{
0x19, 0xd3, 0xe0, 0x8b, 0xbb, 0xad, 0x5f, 0x02,
0x35, 0xa8, 0xa8, 0xf8, 0x1a, 0x7f, 0xa1, 0xe0,
0xf8, 0x50, 0xdd, 0x39, 0x12, 0xe3, 0xc6, 0x55,
0xb4, 0x35, 0xd4, 0x78, 0x6b, 0x93, 0x64, 0xa6
},
},
{
"valid #3",
// Private Key: 5Hz2iVzC9jDcBUGo2EUSPdcvL1s8jxJ6pY95gQX59mHoHx6zi6W
{ 0x16, 0x1f, 0xd0, 0x41, 0x21, 0x50, 0x04, 0x13, 0x90, 0x61, 0xb3, 0xbd, 0xc8, 0x83, 0x09, 0xf0, 0x80, 0x3c, 0x90, 0x45, 0x8e, 0x9a, 0x8c, 0xb2, 0xfb, 0x6a, 0x3c, 0x27, 0x51, 0xcf, 0xd9, 0xca },
// Public key: EOS7Xtaa4y44gYapth4MH5bdtCvdtQvVLdsW7a8thVAuvNAkj8X7i
{ 0x03, 0x5c, 0x50, 0x81, 0xef, 0xa6, 0x46, 0x00, 0x5a, 0xb9, 0xd8, 0x2b, 0xfe, 0xd8, 0xe1, 0x6d, 0x15, 0x42, 0x9e, 0x9a, 0xcb, 0xc9, 0xd6, 0xb3, 0x2e, 0x5a, 0xe3, 0xed, 0xa5, 0x8d, 0x6a, 0x42, 0x6c },
{
0x1b, 0x01, 0x0b, 0xe5, 0xce, 0x6a, 0x49, 0xc7,
0xcd, 0x04, 0x86, 0x0d, 0xef, 0x63, 0x1c, 0x6a,
0xcc, 0xd5, 0x17, 0x47, 0x2e, 0x74, 0x5b, 0xa6,
0xc8, 0xaf, 0x26, 0x1b, 0x15, 0x7e, 0x11, 0xec
},
},
{
"valid #4",
// Private Key: 5KbH8qYyg6f93gHuAdbDmwCVMoarXHqEn5CdbW2VtHTyMsLShwM
{ 0xe9, 0xdb, 0x4d, 0xdc, 0x8e, 0x08, 0xd2, 0x04, 0x75, 0xc2, 0xb6, 0xf7, 0x14, 0x9e, 0xa2, 0x16, 0xd7, 0x2b, 0x34, 0x3b, 0xba, 0xb4, 0x7d, 0xe6, 0xd3, 0x96, 0xc1, 0x63, 0x82, 0xdf, 0x2d, 0x13 },
// Public key: EOS6E12fqQqWLYJS32ffB6LaQYxyDXUQSPfMTMnj6tc5bgntZKcBy
{ 0x02, 0xaf, 0xff, 0xeb, 0xef, 0x47, 0x70, 0x58, 0x2f, 0x9b, 0x66, 0x6c, 0xe0, 0xea, 0x84, 0x32, 0x41, 0xa0, 0x94, 0x36, 0x30, 0x9b, 0xfc, 0xdb, 0x9a, 0x58, 0xdd, 0x0e, 0xe6, 0x3e, 0xd4, 0x5a, 0xcd },
{
0xbc, 0x83, 0xbe, 0xe1, 0x73, 0x82, 0xfb, 0x02,
0x71, 0x25, 0x3b, 0xf5, 0x39, 0x32, 0x55, 0x4e,
0x01, 0x28, 0x5d, 0xf4, 0x02, 0xe8, 0xa2, 0x92,
0x04, 0xf2, 0x95, 0xbc, 0xfa, 0xed, 0x8f, 0xaa
},
},
{
"valid #5",
// Private Key: 5KUb7Y5UqoVggesYJzuNcEmjGtQMpfMiVaEDr9E5K1vNgZqvHok
{ 0xda, 0xaa, 0x85, 0x66, 0xf5, 0xc9, 0x4c, 0xfa, 0x1f, 0x72, 0x30, 0x8e, 0xb8, 0xed, 0x8c, 0xd7, 0x2b, 0x08, 0x80, 0xc8, 0x2b, 0x9d, 0xe9, 0x0a, 0x81, 0xec, 0x8f, 0x3e, 0xe7, 0x75, 0xf0, 0x3c },
// Public key: EOS6uqJC6F7eEMq7SHREhTzRoAT7uumrmHDDYC595CJKeBgPvPojd
{ 0x03, 0x0a, 0x71, 0x8e, 0x48, 0x1a, 0x7a, 0x55, 0x84, 0xb9, 0xaf, 0x24, 0xca, 0x8f, 0x85, 0xcd, 0x0f, 0x55, 0x5c, 0xcf, 0xb3, 0x7b, 0x39, 0x5b, 0xa5, 0xfc, 0xb9, 0xaf, 0x26, 0xc7, 0xc7, 0x88, 0x7b },
{
0xfc, 0xb2, 0x0d, 0xa1, 0x22, 0x6c, 0xcc, 0x59,
0x26, 0x66, 0xba, 0x57, 0xeb, 0x4a, 0xfa, 0x20,
0xff, 0x40, 0x7b, 0xc7, 0x3c, 0x35, 0xb8, 0xaa,
0x72, 0x3e, 0x94, 0xcc, 0x05, 0xe1, 0x51, 0xc7
},
},
{
"valid #6",
// Private Key: 5K4ErsLWnMv53tfFhtp5JF9CYKwmr89Pt2BEpoak1bfdgvHK2fj
{ 0xa3, 0x60, 0xd7, 0x80, 0xdc, 0xe8, 0x3c, 0x5d, 0xc8, 0x61, 0xa1, 0x24, 0x6f, 0x65, 0x9e, 0x16, 0x79, 0x40, 0x10, 0x27, 0xfc, 0x9a, 0x40, 0x3f, 0x2b, 0x40, 0x7a, 0xfd, 0x1e, 0x42, 0xde, 0x84 },
// Public key: EOS5YiBwqnFXqeb5hCmwV9bLHp6Jg5hVnRjzYVApf2DXyRGr7B7kZ
{ 0x02, 0x56, 0xc9, 0x41, 0x90, 0x44, 0x8a, 0xcc, 0x89, 0x91, 0x79, 0xaf, 0x4e, 0x3a, 0x72, 0xa7, 0x24, 0x86, 0x7f, 0xd8, 0x03, 0x07, 0x04, 0x30, 0xd3, 0xf3, 0x6b, 0x20, 0x94, 0x85, 0x78, 0xfc, 0x38 },
{
0xd2, 0xfa, 0xa6, 0x97, 0x12, 0xd7, 0x04, 0x05,
0xe8, 0x60, 0x7e, 0x86, 0x73, 0x69, 0x05, 0x90,
0x97, 0xa2, 0x57, 0xee, 0x12, 0x4b, 0x80, 0x13,
0x04, 0xfa, 0x7d, 0x70, 0xe7, 0xdc, 0x86, 0xb2
},
},
};
libantelope::ec_init();
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ec_signature_t result;
CHECK( libantelope::ecdsa_sign(it->key, &it->dgst, result) == 0 );
// Need to use verify here as different implemententations produces different signatures.
// (i have tested eosjs, eos-go and ofc libantelope)
// However, the signatures are correct and can be validated by all implementations.
//
// Now, how do we know that ecdsa_verify is correct?
// well, in escdsa_verify.cpp there are tests that checks hardcoded signatures generated by different implementations and should be fine.
CHECK( libantelope::ecdsa_verify(&it->dgst, result, it->pub) == 0);
}
}
libantelope::ec_shutdown();
}

189
tests/ec/ecdsa_verify.cpp Normal file
View file

@ -0,0 +1,189 @@
#include <libantelope/ec.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("ec::ecdsa_verify") {
struct testcase {
const char *name;
libantelope::sha256_t dgst;
libantelope::ec_pubkey_t pubkey;
libantelope::ec_signature_t sig;
int expected;
};
std::vector<testcase> tests = {
{
"valid #1",
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
// Public Key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
// SIG_K1_KdgBih1poWj8DYZXwLxMdjaHMzYhuAVp7XshR9ZjrZSubZwsgSpiyUKXu44NmCtKgRFswmqKaioWLTuGZrXwYPsSNCSyyr
{
0x20, 0x44, 0x3f, 0x72, 0x22, 0xfd, 0x7a, 0x1f, 0x56, 0x2d, 0xef, 0x01, 0x55, 0x40, 0xcf, 0x50, 0x6f, 0x5f, 0xdd, 0xfe, 0x71, 0xd7, 0x18, 0xc9, 0xa8, 0xc8, 0xbe, 0x00, 0x96, 0xf8, 0x7c, 0xc7,
0x1f, 0x2d, 0xd0, 0xd1, 0xfc, 0x4a, 0x22, 0x6a, 0x25, 0xc4, 0x7c, 0x99, 0xf9, 0xd8, 0x30, 0xfa, 0x8b, 0x5c, 0x33, 0x36, 0x61, 0xd7, 0xcf, 0x6d, 0x04, 0x97, 0x61, 0x76, 0x47, 0x65, 0x30, 0x7b,
0x66
},
0
},
{
"valid #2 (generated by openssl)",
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
// Public Key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
// SIG_K1_KWRk7botFjDfjNrfk63ud9qEwZbuJ7un8vpEKNzHJ6aSj6eMSVNXmbiXUZV4fsP7PE1kVjYEqtayghNTh1w7ea5ajq4Nrn
{
0x20, 0x0c, 0xd6, 0xe7, 0xfe, 0x3f, 0xfa, 0x01, 0x93, 0xd2, 0x3b, 0xaa, 0xdd, 0xae, 0x5d, 0x91, 0x76, 0x3a, 0x41, 0xb0, 0x14, 0xdc, 0x29, 0xfc, 0x1b, 0x25, 0x77, 0x40, 0x4a, 0x0c, 0x59, 0xc6,
0x77, 0x49, 0x4d, 0xf5, 0x07, 0xc9, 0xfd, 0xf5, 0x11, 0xb5, 0x09, 0xe0, 0xf1, 0x72, 0x4c, 0x08, 0x75, 0xda, 0x47, 0x78, 0x65, 0x12, 0x16, 0x39, 0x1f, 0x6c, 0x9b, 0x22, 0x6c, 0xfa, 0xdd, 0x08,
0xb9
},
0
},
{
"valid #3 (generated by eos-go)",
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
// Public Key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
// SIG_K1_KbRFLvuUdpSwpEYuDQ7EmB3ByGF9i6HdpXS8rxkLeLaKdxFoELh1RVewVyZg2x9ZwstSrJfaLuEgDa8R9RQwkntJFAvQc2
{
0x20, 0x32, 0xfd, 0xf9, 0x6d, 0x2d, 0xa5, 0xda, 0xf1, 0x4d, 0x75, 0x2e, 0xcf, 0x91, 0x06, 0x9d, 0xbb, 0x6a, 0x24, 0x79, 0xd1, 0x70, 0x8d, 0xc7, 0xa2, 0xc2, 0xc0, 0xb4, 0xf6, 0xb7, 0x2b, 0x06,
0xbe, 0x37, 0xc7, 0xbb, 0x3b, 0xe6, 0x47, 0x4c, 0xff, 0x6d, 0x90, 0x02, 0xd4, 0x73, 0x0d, 0x5d, 0xb3, 0x76, 0xc6, 0x52, 0xae, 0xca, 0x90, 0x31, 0xf3, 0xf1, 0x76, 0x5a, 0xbb, 0xad, 0xa5, 0xbc,
0x5f
},
0
},
{
"valid #4 (generated by eosjs)",
{
0xab, 0x53, 0x0a, 0x13, 0xe4, 0x59, 0x14, 0x98,
0x2b, 0x79, 0xf9, 0xb7, 0xe3, 0xfb, 0xa9, 0x94,
0xcf, 0xd1, 0xf3, 0xfb, 0x22, 0xf7, 0x1c, 0xea,
0x1a, 0xfb, 0xf0, 0x2b, 0x46, 0x0c, 0x6d, 0x1d
},
// Public Key: EOS6zjfj9Xjk9CYoucZDptdDZ6317eZd622pVvaYtv5q6gwEs9icD
{ 0x03, 0x15, 0x93, 0x8a, 0x8e, 0x1d, 0x57, 0x84, 0x9f, 0xab, 0x07, 0x18, 0x67, 0xb5, 0x0c, 0xda, 0xb0, 0x77, 0x62, 0x29, 0xb6, 0x43, 0xb8, 0x67, 0x56, 0xc7, 0xb3, 0xe8, 0x7f, 0xe6, 0x08, 0xf8, 0x4b },
// SIG_K1_JzpX9nSsAiQkNLLZ1vSfmDrFQd7228zFhVopCH7S1TnbQ844mDbJeXBF1qifFAcWKBL2mxX7oqj9tgBEPwtu8KY8cWLMim
{
0x1f, 0x2a, 0x88, 0x81, 0x3c, 0xce, 0x11, 0xbf, 0x5a, 0x59, 0x77, 0x8c, 0x32, 0x5b, 0x3b, 0xe8, 0x78, 0x8f, 0x7f, 0x7d, 0x18, 0x1a, 0x6d, 0xb3, 0x7b, 0x36, 0x18, 0x3f, 0xc2, 0xba, 0xb5, 0xab, 0x61, 0x69, 0x89, 0x9c, 0xb9, 0x44, 0x2c, 0x19, 0x2a, 0xa3, 0xb8, 0x3d, 0x2d, 0x09, 0xd0, 0xc4, 0x1a, 0x67, 0xc7, 0xa6, 0x5d, 0xf5, 0x10, 0xb4, 0x75, 0x05, 0x69, 0x4e, 0x4e, 0xc0, 0x5f, 0x34, 0x9e
},
0
},
{
"valid #4",
{
0x19, 0xd3, 0xe0, 0x8b, 0xbb, 0xad, 0x5f, 0x02,
0x35, 0xa8, 0xa8, 0xf8, 0x1a, 0x7f, 0xa1, 0xe0,
0xf8, 0x50, 0xdd, 0x39, 0x12, 0xe3, 0xc6, 0x55,
0xb4, 0x35, 0xd4, 0x78, 0x6b, 0x93, 0x64, 0xa6
},
// Public Key: EOS6tVtKhTpM6yU7kkiRz1AecDJPcBQo2w4x4oytJbJi5PMV2Rcw2
{ 0x03, 0x07, 0x69, 0xbb, 0xa5, 0x2c, 0xd2, 0xe1, 0x3b, 0x3e, 0x0a, 0x40, 0xb3, 0xa2, 0x44, 0xad, 0x71, 0x6e, 0x32, 0x64, 0x9c, 0x3a, 0x64, 0x27, 0x4f, 0x31, 0x86, 0x8a, 0x4c, 0x69, 0x58, 0x86, 0x49 },
// SIG_K1_K4XXx6oSYBzcwzscMstvSxruxdkTCinyN9dnRo4DuBkCCpQbCJQcJmbE7aAmNueBYCccHyyDK5JDfMpvewRF2rGUFtSE2y
{
0x1f, 0x46, 0xde, 0x7a, 0x7e, 0x87, 0xa7, 0xb0, 0x42, 0xce, 0xdc, 0x57, 0xc9, 0x0d, 0x64, 0x4c, 0xc7, 0x4d, 0xe6, 0x19, 0x5d, 0x34, 0x4e, 0xba, 0xfb, 0xdf, 0x26, 0x79, 0xa1, 0xc6, 0x99, 0x98,
0xa7, 0x1f, 0x65, 0xcd, 0xab, 0x2d, 0x19, 0x75, 0x27, 0xdc, 0xb2, 0xc5, 0x46, 0x87, 0x5d, 0xbe, 0xc5, 0x8d, 0xb2, 0xb8, 0x7f, 0x15, 0x47, 0xd7, 0xc7, 0x94, 0x0a, 0xd5, 0x52, 0xd9, 0xe3, 0x93,
0xd7
},
0
},
{
"valid #5",
{
0x1b, 0x01, 0x0b, 0xe5, 0xce, 0x6a, 0x49, 0xc7,
0xcd, 0x04, 0x86, 0x0d, 0xef, 0x63, 0x1c, 0x6a,
0xcc, 0xd5, 0x17, 0x47, 0x2e, 0x74, 0x5b, 0xa6,
0xc8, 0xaf, 0x26, 0x1b, 0x15, 0x7e, 0x11, 0xec
},
// Public Key: EOS7Xtaa4y44gYapth4MH5bdtCvdtQvVLdsW7a8thVAuvNAkj8X7i
{ 0x03, 0x5c, 0x50, 0x81, 0xef, 0xa6, 0x46, 0x00, 0x5a, 0xb9, 0xd8, 0x2b, 0xfe, 0xd8, 0xe1, 0x6d, 0x15, 0x42, 0x9e, 0x9a, 0xcb, 0xc9, 0xd6, 0xb3, 0x2e, 0x5a, 0xe3, 0xed, 0xa5, 0x8d, 0x6a, 0x42, 0x6c },
// SIG_K1_K54CVeQjFREm9Z92jutWESZWb9WQfCRZ2KfMtisfsnxedppeSMxTrZ9fYDLiJTfE79zvLCHb5NysAEcNdh7HiBvtU4Ahhh
{
0x1f, 0x4a, 0xe9, 0x04, 0x20, 0xfa, 0x7b, 0x9d, 0x56, 0xc6, 0x00, 0x5c, 0x83, 0x70, 0xa9, 0x26, 0x41, 0x7d, 0xe8, 0xeb, 0xe7, 0x75, 0xea, 0x6f, 0x75, 0xa7, 0x7c, 0x98, 0x10, 0x27, 0xbf, 0xce,
0x48, 0x41, 0x74, 0xb0, 0xe3, 0xb1, 0x4b, 0x06, 0x2c, 0x53, 0x93, 0xbc, 0x35, 0xea, 0xac, 0xd7, 0x9e, 0x07, 0xa7, 0xa1, 0x2e, 0xac, 0xa0, 0x81, 0x45, 0xdb, 0xd4, 0x53, 0x68, 0xda, 0xaa, 0xc6,
0xfc
},
0
},
{
"not valid #1",
{
0xde, 0x01, 0x64, 0x03, 0x39, 0x01, 0x66, 0x8b,
0xa0, 0x39, 0xef, 0x31, 0x61, 0xc7, 0xc8, 0x9d,
0x15, 0x4b, 0xc6, 0x7b, 0x99, 0x5c, 0xba, 0x9b,
0x23, 0x8a, 0x76, 0x4b, 0x81, 0xf2, 0xff, 0xeb
},
// Public Key: EOS7Xtaa4y44gYapth4MH5bdtCvdtQvVLdsW7a8thVAuvNAkj8X7i
{ 0x03, 0x5c, 0x50, 0x81, 0xef, 0xa6, 0x46, 0x00, 0x5a, 0xb9, 0xd8, 0x2b, 0xfe, 0xd8, 0xe1, 0x6d, 0x15, 0x42, 0x9e, 0x9a, 0xcb, 0xc9, 0xd6, 0xb3, 0x2e, 0x5a, 0xe3, 0xed, 0xa5, 0x8d, 0x6a, 0x42, 0x6c },
// SIG_K1_K54CVeQjFREm9Z92jutWESZWb9WQfCRZ2KfMtisfsnxedppeSMxTrZ9fYDLiJTfE79zvLCHb5NysAEcNdh7HiBvtU4Ahhh
{
0x1f, 0x4a, 0xe9, 0x04, 0x20, 0xfa, 0x7b, 0x9d, 0x56, 0xc6, 0x00, 0x5c, 0x83, 0x70, 0xa9, 0x26, 0x41, 0x7d, 0xe8, 0xeb, 0xe7, 0x75, 0xea, 0x6f, 0x75, 0xa7, 0x7c, 0x98, 0x10, 0x27, 0xbf, 0xce,
0x48, 0x41, 0x74, 0xb0, 0xe3, 0xb1, 0x4b, 0x06, 0x2c, 0x53, 0x93, 0xbc, 0x35, 0xea, 0xac, 0xd7, 0x9e, 0x07, 0xa7, 0xa1, 0x2e, 0xac, 0xa0, 0x81, 0x45, 0xdb, 0xd4, 0x53, 0x68, 0xda, 0xaa, 0xc6,
0xfc
},
-1
},
{
"not valid #2",
{
0xa7, 0xf7, 0x89, 0x36, 0xea, 0xb7, 0x95, 0xa7,
0x71, 0xaa, 0x73, 0xb5, 0xf6, 0xb8, 0xa0, 0x40,
0xe5, 0x4f, 0xb3, 0x87, 0xff, 0xd9, 0xb6, 0x4e,
0x30, 0x4c, 0xa3, 0x78, 0xab, 0x68, 0x86, 0x24
},
// EOS5AxTzvLZ7mRPvo1Ju9nCdB31PruYHE9uar8pF6D3CvZQGWcHq8
{ 0x02, 0x25, 0x64, 0x31, 0x9d, 0x41, 0x46, 0x82, 0xeb, 0x60, 0xed, 0x17, 0xe9, 0x8a, 0xd1, 0x21, 0x60, 0xc4, 0x65, 0xe7, 0x7e, 0x73, 0x2e, 0x45, 0xf0, 0x78, 0x8b, 0x7f, 0x43, 0x30, 0x71, 0xbc, 0x34 },
// SIG_K1_K4QgBwbwAP879cjV66LjPmEBoVA3FXesYp6KxRbbshVeQRSJmgreFkBYx3eBTHNdJx2dxZatx9sRJvwh1JY2F2U6APmHKE
{
0x1f, 0x45, 0xf7, 0x1c, 0x26, 0x39, 0x8a, 0x32, 0x5c, 0xfe, 0xb4, 0xc3, 0x46, 0x51, 0x22, 0x88, 0xef, 0x3b, 0xad, 0xb2, 0x5f, 0x29, 0x23, 0xab, 0x26, 0xbd, 0x3a, 0x1f, 0xbf, 0x24, 0x12, 0x36,
0x3f, 0x73, 0xfe, 0x26, 0xfc, 0x3b, 0xd0, 0xca, 0xf1, 0xdd, 0x9d, 0x80, 0x68, 0xb9, 0x66, 0x1d, 0xc4, 0xf9, 0x6f, 0x07, 0x9e, 0xe9, 0xf2, 0x4d, 0x4e, 0xff, 0xc1, 0xd1, 0xe1, 0xf1, 0xbc, 0x6f,
0x72
},
-1
},
{
"not valid #3",
{
0x48, 0xd7, 0xd3, 0x83,0x9c, 0xa2, 0x82, 0xde,
0xb6, 0x9a, 0xb8, 0x34,0x36, 0xb0, 0x9f, 0x19,
0xbb, 0xdf, 0x2b, 0xb5,0x39, 0x42, 0x92, 0x32,
0x33, 0x34, 0x84, 0xdd,0xba, 0xbd, 0x95, 0x43
},
// EOS5vhJWLeBjQAiTZxWdnFkttUDWANurEka69La2nu8fp2gSi5eQk
{ 0x02, 0x88, 0xb4, 0x83, 0x3a, 0x86, 0x18, 0xd3, 0xb2, 0x2b, 0xbe, 0xe6, 0x59, 0x3d, 0xf2, 0x41, 0xf6, 0xed, 0x86, 0x40, 0xe6, 0x19, 0xc8, 0x45, 0x03, 0x78, 0x57, 0xde, 0xcb, 0x2a, 0xd7, 0xc2, 0xf0 },
// SIG_K1_K2XJBWCnx98kr8N3fe25BFDpsc8M4nERkzYSWpVAMoXBv9Nw83xDTTSErstUnh25scMGDHn9TsAqNirNruWRM3jzdKGe7m
{
0x1f, 0x37, 0x8c, 0xf2, 0xbe, 0xb1, 0x18, 0xb7, 0x80, 0x60, 0xd5, 0x60, 0x9f, 0xee, 0x7b, 0x36, 0xcc, 0x42, 0x9c, 0x12, 0xb8, 0xd1, 0x5f, 0x62, 0xad, 0x88, 0x4f, 0x6c, 0x65, 0xa1, 0x70, 0x03,
0x30, 0x1b, 0x65, 0x80, 0xc9, 0xe9, 0xba, 0xfe, 0xcf, 0xcc, 0xdb, 0x2d, 0xf0, 0x37, 0x1b, 0x00, 0x39, 0xb5, 0x16, 0x7c, 0xde, 0xec, 0x4b, 0xec, 0xc1, 0xf4, 0xf2, 0x6f, 0x1b, 0xfa, 0x80, 0x9d,
0x81
},
-1
},
};
libantelope::ec_init();
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
CHECK( libantelope::ecdsa_verify(&it->dgst, it->sig, it->pubkey) == it->expected );
}
}
libantelope::ec_shutdown();
}

18
tests/ec/generate.cpp Normal file
View file

@ -0,0 +1,18 @@
#include <libantelope/ec.hpp>
#include <doctest.h>
TEST_CASE("ec::generate") {
libantelope::ec_init();
libantelope::ec_pubkey_t result;
libantelope::ec_keypair pair;
CHECK(libantelope::ec_generate_key(&pair) == 0);
// Can't test much because... well the private key should be random :)
// But alteast verify that the public key belongs to the private key.
CHECK(libantelope::ec_get_publickey(&pair.secret, &result) == 0);
CHECK( result == pair.pub );
libantelope::ec_shutdown();
}

140
tests/ec/pubkey.cpp Normal file
View file

@ -0,0 +1,140 @@
#include <libantelope/ec.hpp>
#include <libantelope/WIF.hpp>
#include <vector>
#include <doctest.h>
TEST_CASE("ec::pubkey") {
struct testcase {
std::string name;
libantelope::ec_privkey_t priv;
libantelope::ec_pubkey_t expected;
};
std::vector<struct testcase> tests {
{
"valid #1",
// 5KWCD8zm3nHbYSZhq4rfnX3VxXGnKzcorwnqi9QoYvkyj6kSVYr
{
0xde, 0x4f, 0x8a, 0x59, 0x12, 0x2d, 0xee, 0x24,
0xf3, 0x75, 0x39, 0x00, 0xa5, 0x2c, 0x64, 0x58,
0x31, 0x5a, 0x1e, 0xea, 0x14, 0x30, 0x1b, 0x4d,
0x61, 0x6d, 0x2e, 0xab, 0x48, 0x48, 0xf3, 0xe1
},
// EOS6bmyyDPYt3ZXZF1VGKnKN4pbMuZPnFBoK7xkvXmyYUnXmBTSYq
{
0x02, 0xe1, 0x71, 0x50, 0x31, 0x19, 0x5b, 0xe8,
0xe0, 0x92, 0xec, 0x61, 0xfc, 0x93, 0xa8, 0x81,
0x2a, 0x1e, 0x83, 0x74, 0xc3, 0x5f, 0x5b, 0xe0,
0xfd, 0xb7, 0xd4, 0x32, 0xa6, 0xb9, 0x87, 0x61,
0x1b
}
},
{
"valid #2",
// 5KCjzBFK5jiUdwjwFzqymHbVoUEjhbK86CGiDKtuwXrP2xyDZLY
{
0xb6, 0xae, 0xfa, 0x9a, 0xc2, 0x1e, 0x56, 0xb8,
0x70, 0xa1, 0x14, 0x4e, 0x7f, 0xaa, 0x70, 0x30,
0x5b, 0x6d, 0xc4, 0x94, 0xd5, 0x05, 0xba, 0xf7,
0x77, 0x82, 0x93, 0xf2, 0x91, 0x15, 0x9b, 0x00
},
// EOS75mzwjLQAKaVdtGKe5MjfjQY9UJRZqa1iGcpkvZQ6jLE8ptE2j
{
0x03, 0x21, 0x05, 0x4d, 0x71, 0x0d, 0x08, 0x32,
0x67, 0xf9, 0xca, 0xa4, 0x16, 0x9e, 0x42, 0xca,
0xa6, 0xa2, 0x90, 0xe4, 0xbf, 0xe4, 0xc6, 0x79,
0x30, 0x69, 0x3d, 0xe7, 0x00, 0x76, 0x40, 0xea,
0x11
}
},
{
"valid #3",
// 5KfQfL9BKMsW1eNBYqFRJ7SgDAFzJcNH8UsXd1RWHdFUzUxJEKf
{
0xf3, 0x3b, 0xd8, 0x45, 0xaf, 0x53, 0x1e, 0x51,
0x88, 0x62, 0x03, 0x46, 0xa9, 0x13, 0x54, 0x5b,
0xf9, 0x6c, 0x95, 0x23, 0x9b, 0xbb, 0xbe, 0xd9,
0xff, 0x91, 0xde, 0x0d, 0x5a, 0x38, 0x9e, 0x8f
},
// EOS6DqV9RU4D5bN8TMg6G4Gcby56UxxBMfk21XotQ2TDuzDyA3QjN
{
0x02, 0xaf, 0xa0, 0x46, 0xa0, 0x36, 0x8a, 0xf4,
0x55, 0xd1, 0x60, 0x8d, 0xf8, 0x6d, 0x2f, 0x73,
0x24, 0xe4, 0x6e, 0xa9, 0xf2, 0x37, 0x47, 0x83,
0x6b, 0x74, 0x3a, 0x0e, 0xb6, 0x8f, 0x6b, 0xed,
0x51
}
},
{
"valid #4",
// 5KfdPV9x9BNqrDQoB4XfX8PyGyA6Nhwi6DtAG9KmEpyRNomNFoP
{
0xf3, 0xbb, 0x64, 0xc9, 0xc8, 0x8c, 0xb1, 0x8c,
0xc2, 0xbc, 0xd7, 0x03, 0x5c, 0x53, 0xa0, 0xd6,
0xeb, 0x53, 0xea, 0xb5, 0x20, 0x45, 0x92, 0xb1,
0xed, 0x9b, 0x1e, 0x6a, 0x01, 0xe9, 0x1a, 0xdd
},
// EOS7B1MBLsYbo7dppCwPG6L25AaS1wTGeaVtjLFKo8ue1Yo317G65
{
0x03, 0x2c, 0xe5, 0x7b, 0x76, 0x3d, 0xb6, 0x33,
0x10, 0xb2, 0x0b, 0x04, 0xb5, 0x32, 0xeb, 0xad,
0x0f, 0x27, 0x18, 0x77, 0xe8, 0x5a, 0x32, 0xc3,
0x76, 0x12, 0x1a, 0x85, 0xf3, 0x61, 0x3b, 0x5d,
0x4b
}
},
{
"valid #5",
// 5KRrtvA13uHmKVy6ofjEQQvcciGKeC2NTGYeoAGMYrNp56dn98y
{
0xd4, 0x78, 0xdb, 0x03, 0x2d, 0xf3, 0xfa, 0xad,
0x21, 0xa0, 0xdd, 0x36, 0xb1, 0xf5, 0xf5, 0x13,
0xc4, 0x8a, 0xe1, 0xa8, 0x37, 0xfa, 0x2e, 0x1b,
0xaa, 0x69, 0x2c, 0xf8, 0xf9, 0x5e, 0xb1, 0x87
},
// EOS54o7UHWAPXxHtrWS3uoZDJ6npud84R9w8XVMPYZNKhKiQgkQ1k
{
0x02, 0x17, 0x66, 0xc5, 0xb1, 0x59, 0xd7, 0xbe,
0x10, 0x82, 0xc5, 0x7f, 0xa3, 0xd2, 0xc8, 0x21,
0xd8, 0x48, 0x09, 0x34, 0xdf, 0x66, 0x61, 0x23,
0x6d, 0xb0, 0x85, 0x73, 0x77, 0xe7, 0xd8, 0xe8,
0x3b
}
},
{
"valid #6",
// 5JXVrPTX5WvZ524wEVYoThGFUJZkpkemQPLDa5MCrRo3MoGatyC
{
0x5d, 0x93, 0x6e, 0x47, 0x44, 0x35, 0x85, 0x33,
0xbc, 0xde, 0xff, 0xa7, 0x88, 0xe7, 0x9c, 0x6f,
0x53, 0xbc, 0x8e, 0xbf, 0x5e, 0x8e, 0xdb, 0x7e,
0x36, 0x2b, 0xdf, 0x11, 0xff, 0x77, 0x59, 0x98
},
// EOS7d5Nq7w2xscT6V6qZkDCJy2EnMFE8TGuQ9u2LyBE1fSZFiitrp
{
0x03, 0x68, 0x17, 0x1e, 0x3b, 0x22, 0x0a, 0x6b,
0x6b, 0xb6, 0xfa, 0x1b, 0x32, 0xdd, 0xce, 0xf6,
0x34, 0x7a, 0x34, 0x62, 0xb7, 0xf9, 0xea, 0x1c,
0xa2, 0xf9, 0xf0, 0x2c, 0x50, 0x3e, 0xff, 0xf3,
0xfa
}
}
};
libantelope::ec_init();
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name.c_str()) {
libantelope::ec_pubkey_t result;
CHECK( libantelope::ec_get_publickey(&it->priv, &result) == 0 );
CHECK( result == it->expected );
}
}
libantelope::ec_shutdown();
}

118
tests/hash/ripemd160.cpp Normal file
View file

@ -0,0 +1,118 @@
#include <libantelope/hash/ripemd160.hpp>
#include <string>
#include <vector>
#include <testing.h>
TEST_CASE("hash::ripemd160::ripemd160") {
struct testcase {
const char *name;
std::string input;
libantelope::ripemd160_t expected;
bool valid;
};
std::vector<testcase> tests = {
{
"valid #1",
"Morbi at egestas risus. Praesent blandit pharetra urna, nec porttitor risus sodales eu. Cras et volutpat elit, porta dapibus ipsum. Donec facilisis, eros nec imperdiet tristique, purus eros malesuada neque, quis interdum nisl risus nec leo.",
{
0xd9, 0x6a, 0x48, 0xf8, 0x2b, 0x39, 0xa9, 0x9f,
0x22, 0xba, 0x3e, 0x01, 0x58, 0x5b, 0x15, 0xc7,
0x7b, 0x0e, 0x5f, 0x50,
},
true
},
{
"valid #2",
"Donec eget mattis velit, vel vulputate sem. Suspendisse vulputate dolor vel est facilisis congue. Nulla non leo nulla. Proin lorem elit, sagittis eget congue in, pellentesque sed nisi. In pulvinar tortor fermentum suscipit varius.",
{
0x87, 0x1b, 0x87, 0xde, 0x2e, 0xb6, 0x8b, 0xb6,
0xdc, 0x29, 0xe7, 0x40, 0xc3, 0xd1, 0x99, 0x42,
0xad, 0x1a, 0xe3, 0x57
},
true
}
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ripemd160_t dgst;
CHECK( libantelope::ripemd160((const unsigned char*) it->input.c_str(), it->input.size(), &dgst) == &dgst );
CHECK_PRED(doctest::toString(dgst), doctest::toString(it->expected), it->valid);
}
}
}
TEST_CASE("hash::ripemd160::init/update/final") {
struct testcase {
const char *name;
std::vector<std::string> inputs;
libantelope::ripemd160_t expected;
#ifdef _MSC_VER
char _; // ripemd160_t can't be last, wierd compiler bug on MSVC
#endif
};
std::vector<testcase> tests = {
{
"valid #1",
{
"tortor in congue luctus, tortor sapien condimentum quam, ac congue enim lacus vitae erat. Mauris dapibus eros bibendum",
"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae",
},
{
0x30, 0x77, 0xaf, 0x6b, 0x43, 0x0b, 0x94, 0x8d,
0x59, 0x4e, 0xc7, 0xbb, 0x1a, 0x2b, 0xc3, 0x08,
0xaa, 0xf0, 0x75, 0x3a
}
},
{
"valid #2",
{
"Cras suscipit, mi sit amet pretium blandit, massa felis aliquet eros",
"Aenean efficitur nibh quis enim mollis blandit",
"Vestibulum posuere tempus mi nec cursus"
},
{
0xf1, 0xcf, 0xea, 0xf7, 0xef, 0x3a, 0x0f, 0x80,
0x26, 0x75, 0x40, 0x75, 0xe0, 0x9d, 0x89, 0x05,
0xd1, 0x29, 0xe5, 0xf6
}
},
{
"valid #3",
{
"Donec nec blandit dui. Nulla et tempus odio, id fermentum neque. Nam vitae nunc leo. Aliquam dictum velit nec neque dignissim maximus nec at tellus",
"Proin elementum porttitor odio, ut ullamcorper justo rutrum in. Proin dignissim nec diam a eleifend. Duis consequat ultrices purus sed finibus",
"Donec eget ante dictum, scelerisque metus eget, mollis velit. Curabitur elementum fermentum lorem, a fringilla velit ultrices non"
},
{
0xbb, 0x25, 0x58, 0xa9, 0xd0, 0xc1, 0x23, 0xef,
0x55, 0xac, 0x2d, 0x8c, 0xd5, 0xd6, 0xe1, 0x49,
0x00, 0x5d, 0x86, 0xe8
}
},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::ripemd160_ctx_t ctx;
libantelope::ripemd160_t dgst;
CHECK_EQ(libantelope::ripemd160_init(&ctx), 1);
for (auto in_it = it->inputs.begin(); in_it != it->inputs.end(); in_it++ ) {
CHECK_EQ(libantelope::ripemd160_update(&ctx, (const unsigned char*) in_it->c_str(), in_it->size()), 1);
}
CHECK_EQ(libantelope::ripemd160_final(&ctx, &dgst), 1);
CHECK( doctest::toString(dgst) == doctest::toString(it->expected) );
}
}
}

180
tests/hash/sha256.cpp Normal file
View file

@ -0,0 +1,180 @@
#include <libantelope/hash/sha256.hpp>
#include <string>
#include <vector>
#include <testing.h>
TEST_CASE("hash::sha256::sha256") {
struct testcase {
const char *name;
std::string input;
libantelope::sha256_t expected;
bool valid;
};
std::vector<testcase> tests = {
{
"valid #1",
"Suspendisse ut tincidunt quam. Praesent scelerisque risus vitae est consectetur, sed facilisis sem luctus. Praesent aliquet eros quis leo sodales, eget blandit diam scelerisque.",
{
0x1e, 0x54, 0x96, 0x86, 0x2f, 0x39, 0x44, 0xea,
0x42, 0xa9, 0x0f, 0xad, 0x56, 0x79, 0x4b, 0x77,
0x8f, 0xcc, 0x54, 0xf7, 0x7a, 0x32, 0x60, 0x37,
0x4b, 0xac, 0xd5, 0x65, 0x74, 0xf7, 0xcf, 0x6c
},
true
},
{
"valid #2",
"Phasellus consectetur augue vitae massa vulputate placerat. Pellentesque nec eros a velit bibendum venenatis sit amet et augue. Morbi malesuada facilisis consequat.",
{
0x7c, 0x79, 0x4a, 0xf4, 0x9b, 0x5b, 0xb4, 0x0c,
0xef, 0x4f, 0xaa, 0x65, 0xa4, 0x7c, 0x5f, 0xc5,
0x95, 0x69, 0x49, 0x99, 0x6b, 0x08, 0x9b, 0xc0,
0x40, 0x2d, 0x57, 0x8a, 0x90, 0x02, 0x42, 0x32,
},
true
}
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::sha256_t dgst;
CHECK( libantelope::sha256((const unsigned char*) it->input.c_str(), it->input.size(), &dgst) == &dgst );
CHECK_PRED(doctest::toString(dgst), doctest::toString(it->expected), it->valid);
}
}
}
TEST_CASE("hash::sha256::sha256d") {
struct testcase {
const char *name;
std::string input;
libantelope::sha256_t expected;
#ifdef _MSC_VER
char _; // sha256_t can't be last, wierd compiler bug on MSVC
#endif
};
std::vector<testcase> tests = {
{
"valid #1",
"Suspendisse ut tincidunt quam. Praesent scelerisque risus vitae est consectetur, sed facilisis sem luctus. Praesent aliquet eros quis leo sodales, eget blandit diam scelerisque.",
{
0x4b, 0x6f, 0xa1, 0xf6, 0x30, 0x1e, 0xbe, 0x4a,
0xc7, 0xef, 0x1e, 0x55, 0x3e, 0xdb, 0xc1, 0x31,
0x1f, 0x6b, 0xf5, 0xc8, 0x04, 0xe9, 0x0e, 0xe3,
0xbe, 0x66, 0x01, 0xbf, 0x70, 0x9f, 0x8e, 0x80,
}
},
{
"valid #2",
"Vivamus ut elementum justo. Vestibulum lobortis rutrum libero sollicitudin aliquet. Nullam tempor urna non odio iaculis, sed pretium quam porttitor. Pellentesque pretium, justo vitae tristique porttitor, diam massa pulvinar neque, sed lacinia mi nulla sed nisi.",
{
0x1f, 0x3f, 0x1c, 0x48, 0xf6, 0xee, 0x24, 0x1f,
0x6c, 0x41, 0x86, 0x69, 0xe3, 0x2f, 0x5e, 0x4d,
0xa5, 0x51, 0x04, 0x8b, 0x11, 0x35, 0x47, 0xad,
0x7e, 0xd9, 0xfb, 0x2e, 0x59, 0xee, 0x66, 0x21,
}
},
{
"valid #3",
"Praesent ultrices consequat risus luctus faucibus.",
{
0xd5, 0x5f, 0x9c, 0xda, 0x2d, 0x93, 0x32, 0xc2,
0x9b, 0xb1, 0xbb, 0x14, 0x55, 0x80, 0x72, 0xb7,
0xba, 0x13, 0xa8, 0xc6, 0xa6, 0xbc, 0x65, 0xfc,
0x49, 0xe0, 0x3b, 0x23, 0x04, 0x2a, 0x92, 0x8d,
}
}
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::sha256_t dgst;
CHECK( libantelope::sha256d((const unsigned char*) it->input.c_str(), it->input.size(), &dgst) == &dgst );
CHECK( doctest::toString(dgst) == doctest::toString(it->expected) );
}
}
}
TEST_CASE("hash::sha256::init/update/final") {
struct testcase {
const char *name;
std::vector<std::string> inputs;
libantelope::sha256_t expected;
#ifdef _MSC_VER
char _; // sha256_t can't be last, wierd compiler bug on MSVC
#endif
};
std::vector<testcase> tests = {
{
"valid #1",
{
"Donec vestibulum enim commodo, faucibus nisi non, mattis quam.",
"Nam sed nunc dapibus, auctor risus placerat, aliquet dolor",
},
{
0x48, 0xc2, 0x34, 0x93, 0x3d, 0xae, 0x0d, 0xd0,
0x28, 0xff, 0x5c, 0xa0, 0xca, 0xb1, 0x0a, 0xa3,
0xe2, 0xa0, 0xa4, 0x7e, 0xb2, 0x71, 0xa5, 0x28,
0x41, 0x03, 0x72, 0x20, 0xb5, 0x23, 0xc3, 0x67,
}
},
{
"valid #2",
{
"In tempus, lectus ac molestie venenatis, enim purus suscipit tortor",
"sed sodales massa condimentum a",
"Integer sit amet pretium magna",
"Aenean non accumsan eros. Donec imperdiet justo tempor magna tincidunt malesuada",
"Duis eu tortor ac massa sagittis elementum"
},
{
0xfb, 0x12, 0x31, 0x9c, 0x2c, 0xe4, 0x94, 0x29,
0xc9, 0xd3, 0xc7, 0x84, 0x0c, 0x58, 0x3d, 0x4c,
0xde, 0xb5, 0x36, 0x59, 0x46, 0x69, 0xe1, 0x63,
0xc5, 0x75, 0xb6, 0x94, 0x41, 0x5a, 0xd4, 0x62,
}
},
{
"valid #3",
{
"Donec tempus pellentesque lobortis. Integer pellentesque feugiat enim ac suscipit. Curabitur urna quam, condimentum sed bibendum eu",
"Nullam lacinia ligula at ex gravida fermentum. Integer scelerisque accumsan iaculis. Suspendisse quis eros ut orci sollicitudin dignissim",
"Nulla ligula tortor, tristique eget feugiat non, vehicula sit amet velit. Proin fermentum sagittis tincidunt. Nullam condimentum dapibus magna",
},
{
0x19, 0xfb, 0x71, 0xb1, 0x47, 0x01, 0x7f, 0xf5,
0xeb, 0xda, 0xc2, 0xd8, 0xe7, 0xab, 0xc9, 0xcb,
0xea, 0x7d, 0x13, 0xa0, 0x2e, 0xe8, 0x48, 0x94,
0x67, 0xc5, 0x14, 0xbf, 0x7d, 0x6f, 0x96, 0x83,
}
},
};
for(auto it = tests.begin(); it != tests.end(); it++) {
SUBCASE(it->name) {
libantelope::sha256_ctx_t ctx;
libantelope::sha256_t dgst;
CHECK_EQ(libantelope::sha256_init(&ctx), 1);
for (auto in_it = it->inputs.begin(); in_it != it->inputs.end(); in_it++ ) {
CHECK_EQ(libantelope::sha256_update(&ctx, (const unsigned char*) in_it->c_str(), in_it->size()), 1);
}
CHECK_EQ(libantelope::sha256_final(&ctx, &dgst), 1);
CHECK( doctest::toString(dgst) == doctest::toString(it->expected) );
}
}
}

7091
tests/include/doctest.h Normal file

File diff suppressed because it is too large Load diff

8
tests/include/testing.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef LIBANTELOPE_TESTING_H
#define LIBANTELOPE_TESTING_H
#include <doctest.h>
#define CHECK_PRED(a,b,pred) if (pred) { CHECK_EQ((a), (b)); } else { CHECK_NE((a), (b)); }
#endif /* LIBANTELOPE_TESTING_H */

11
tests/main.cpp Normal file
View file

@ -0,0 +1,11 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include <doctest.h>
int main(int argc, char** argv) {
doctest::Context context;
context.applyCommandLine(argc, argv);
return context.run();
}

16
vendor/secp256k1/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,16 @@
set(TARGET secp256k1)
add_library(${TARGET} OBJECT
repo/src/secp256k1.c
repo/src/precomputed_ecmult.c
repo/src/precomputed_ecmult_gen.c
)
target_compile_definitions(${TARGET}
PRIVATE ENABLE_MODULE_RECOVERY
)
target_include_directories(${TARGET}
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/repo/include
)

409
vendor/secp256k1/repo/.cirrus.yml vendored Normal file
View file

@ -0,0 +1,409 @@
env:
### cirrus config
CIRRUS_CLONE_DEPTH: 1
### compiler options
HOST:
WRAPPER_CMD:
# Specific warnings can be disabled with -Wno-error=foo.
# -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual.
WERROR_CFLAGS: -Werror -pedantic-errors
MAKEFLAGS: -j4
BUILD: check
### secp256k1 config
ECMULTWINDOW: auto
ECMULTGENPRECISION: auto
ASM: no
WIDEMUL: auto
WITH_VALGRIND: yes
EXTRAFLAGS:
### secp256k1 modules
EXPERIMENTAL: no
ECDH: no
RECOVERY: no
SCHNORRSIG: no
### test options
SECP256K1_TEST_ITERS:
BENCH: yes
SECP256K1_BENCH_ITERS: 2
CTIMETESTS: yes
# Compile and run the tests
EXAMPLES: yes
# https://cirrus-ci.org/pricing/#compute-credits
credits_snippet: &CREDITS
# Don't use any credits for now.
use_compute_credits: false
cat_logs_snippet: &CAT_LOGS
always:
cat_tests_log_script:
- cat tests.log || true
cat_noverify_tests_log_script:
- cat noverify_tests.log || true
cat_exhaustive_tests_log_script:
- cat exhaustive_tests.log || true
cat_ctime_tests_log_script:
- cat ctime_tests.log || true
cat_bench_log_script:
- cat bench.log || true
cat_config_log_script:
- cat config.log || true
cat_test_env_script:
- cat test_env.log || true
cat_ci_env_script:
- env
merge_base_script_snippet: &MERGE_BASE
merge_base_script:
- if [ "$CIRRUS_PR" = "" ]; then exit 0; fi
- git fetch --depth=1 $CIRRUS_REPO_CLONE_URL "pull/${CIRRUS_PR}/merge"
- git checkout FETCH_HEAD # Use merged changes to detect silent merge conflicts
linux_container_snippet: &LINUX_CONTAINER
container:
dockerfile: ci/linux-debian.Dockerfile
# Reduce number of CPUs to be able to do more builds in parallel.
cpu: 1
# Gives us more CPUs for free if they're available.
greedy: true
# More than enough for our scripts.
memory: 1G
task:
name: "x86_64: Linux (Debian stable)"
<< : *LINUX_CONTAINER
matrix: &ENV_MATRIX
- env: {WIDEMUL: int64, RECOVERY: yes}
- env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes}
- env: {WIDEMUL: int128}
- env: {WIDEMUL: int128_struct}
- env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes}
- env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes}
- env: {WIDEMUL: int128, ASM: x86_64}
- env: { RECOVERY: yes, SCHNORRSIG: yes}
- env: {CTIMETESTS: no, RECOVERY: yes, ECDH: yes, SCHNORRSIG: yes, CPPFLAGS: -DVERIFY}
- env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETESTS: no, BENCH: no}
- env: {CPPFLAGS: -DDETERMINISTIC}
- env: {CFLAGS: -O0, CTIMETESTS: no}
- env: { ECMULTGENPRECISION: 2, ECMULTWINDOW: 2 }
- env: { ECMULTGENPRECISION: 8, ECMULTWINDOW: 4 }
matrix:
- env:
CC: gcc
- env:
CC: clang
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "i686: Linux (Debian stable)"
<< : *LINUX_CONTAINER
env:
HOST: i686-linux-gnu
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
matrix:
- env:
CC: i686-linux-gnu-gcc
- env:
CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "arm64: macOS Ventura"
macos_instance:
image: ghcr.io/cirruslabs/macos-ventura-base:latest
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
# Cirrus gives us a fixed number of 4 virtual CPUs. Not that we even have that many jobs at the moment...
MAKEFLAGS: -j5
matrix:
<< : *ENV_MATRIX
env:
ASM: no
WITH_VALGRIND: no
CTIMETESTS: no
matrix:
- env:
CC: gcc
- env:
CC: clang
brew_script:
- brew install automake libtool gcc
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
<< : *CREDITS
task:
name: "s390x (big-endian): Linux (Debian stable, QEMU)"
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: qemu-s390x
SECP256K1_TEST_ITERS: 16
HOST: s390x-linux-gnu
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
<< : *MERGE_BASE
test_script:
# https://sourceware.org/bugzilla/show_bug.cgi?id=27008
- rm /etc/ld.so.cache
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "ARM32: Linux (Debian stable, QEMU)"
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: qemu-arm
SECP256K1_TEST_ITERS: 16
HOST: arm-linux-gnueabihf
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
matrix:
- env: {}
- env: {EXPERIMENTAL: yes, ASM: arm}
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "ARM64: Linux (Debian stable, QEMU)"
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: qemu-aarch64
SECP256K1_TEST_ITERS: 16
HOST: aarch64-linux-gnu
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "ppc64le: Linux (Debian stable, QEMU)"
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: qemu-ppc64le
SECP256K1_TEST_ITERS: 16
HOST: powerpc64le-linux-gnu
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: wine
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
matrix:
- name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
env:
HOST: x86_64-w64-mingw32
- name: "i686 (mingw32-w64): Windows (Debian stable, Wine)"
env:
HOST: i686-w64-mingw32
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
<< : *LINUX_CONTAINER
env:
WRAPPER_CMD: wine
WERROR_CFLAGS: -WX
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
EXPERIMENTAL: yes
SCHNORRSIG: yes
CTIMETESTS: no
# Use a MinGW-w64 host to tell ./configure we're building for Windows.
# This will detect some MinGW-w64 tools but then make will need only
# the MSVC tools CC, AR and NM as specified below.
HOST: x86_64-w64-mingw32
CC: /opt/msvc/bin/x64/cl
AR: /opt/msvc/bin/x64/lib
NM: /opt/msvc/bin/x64/dumpbin -symbols -headers
# Set non-essential options that affect the CLI messages here.
# (They depend on the user's taste, so we don't want to set them automatically in configure.ac.)
CFLAGS: -nologo -diagnostics:caret
LDFLAGS: -Xlinker -Xlinker -Xlinker -nologo
matrix:
- name: "x86_64 (MSVC): Windows (Debian stable, Wine)"
- name: "x86_64 (MSVC): Windows (Debian stable, Wine, int128_struct)"
env:
WIDEMUL: int128_struct
- name: "x86_64 (MSVC): Windows (Debian stable, Wine, int128_struct with __(u)mulh)"
env:
WIDEMUL: int128_struct
CPPFLAGS: -DSECP256K1_MSVC_MULH_TEST_OVERRIDE
- name: "i686 (MSVC): Windows (Debian stable, Wine)"
env:
HOST: i686-w64-mingw32
CC: /opt/msvc/bin/x86/cl
AR: /opt/msvc/bin/x86/lib
NM: /opt/msvc/bin/x86/dumpbin -symbols -headers
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
# Sanitizers
task:
<< : *LINUX_CONTAINER
env:
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: no
matrix:
- name: "Valgrind (memcheck)"
container:
cpu: 2
env:
# The `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
WRAPPER_CMD: "valgrind --error-exitcode=42"
SECP256K1_TEST_ITERS: 2
- name: "UBSan, ASan, LSan"
container:
memory: 2G
env:
CFLAGS: "-fsanitize=undefined,address -g"
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
ASAN_OPTIONS: "strict_string_checks=1:detect_stack_use_after_return=1:detect_leaks=1"
LSAN_OPTIONS: "use_unaligned=1"
SECP256K1_TEST_ITERS: 32
# Try to cover many configurations with just a tiny matrix.
matrix:
- env:
ASM: auto
- env:
ASM: no
ECMULTGENPRECISION: 2
ECMULTWINDOW: 2
matrix:
- env:
CC: clang
- env:
HOST: i686-linux-gnu
CC: i686-linux-gnu-gcc
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
# Memory sanitizers
task:
<< : *LINUX_CONTAINER
name: "MSan"
env:
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
CTIMETESTS: yes
CC: clang
SECP256K1_TEST_ITERS: 32
ASM: no
WITH_VALGRIND: no
container:
memory: 2G
matrix:
- env:
CFLAGS: "-fsanitize=memory -g"
- env:
ECMULTGENPRECISION: 2
ECMULTWINDOW: 2
CFLAGS: "-fsanitize=memory -g -O3"
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "C++ -fpermissive (entire project)"
<< : *LINUX_CONTAINER
env:
CC: g++
CFLAGS: -fpermissive -g
CPPFLAGS: -DSECP256K1_CPLUSPLUS_TEST_OVERRIDE
WERROR_CFLAGS:
ECDH: yes
RECOVERY: yes
SCHNORRSIG: yes
<< : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
task:
name: "C++ (public headers)"
<< : *LINUX_CONTAINER
test_script:
- g++ -Werror include/*.h
- clang -Werror -x c++-header include/*.h
- /opt/msvc/bin/x64/cl.exe -c -WX -TP include/*.h
task:
name: "sage prover"
<< : *LINUX_CONTAINER
test_script:
- cd sage
- sage prove_group_implementations.sage
task:
name: "x86_64: Windows (VS 2022)"
windows_container:
image: cirrusci/windowsservercore:visualstudio2022
cpu: 4
memory: 3840MB
env:
PATH: '%CIRRUS_WORKING_DIR%\build\src\RelWithDebInfo;%PATH%'
x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"'
# Ignore MSBuild warning MSB8029.
# See: https://learn.microsoft.com/en-us/visualstudio/msbuild/errors/msb8029?view=vs-2022
IgnoreWarnIntDirInTempDetected: 'true'
merge_script:
- PowerShell -NoLogo -Command if ($env:CIRRUS_PR -ne $null) { git fetch $env:CIRRUS_REPO_CLONE_URL pull/$env:CIRRUS_PR/merge; git reset --hard FETCH_HEAD; }
configure_script:
- '%x64_NATIVE_TOOLS%'
- cmake -G "Visual Studio 17 2022" -A x64 -S . -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON
build_script:
- '%x64_NATIVE_TOOLS%'
- cmake --build build --config RelWithDebInfo -- -property:UseMultiToolTask=true;CL_MPcount=5
check_script:
- '%x64_NATIVE_TOOLS%'
- ctest --test-dir build -j 5
- build\src\RelWithDebInfo\bench_ecmult.exe
- build\src\RelWithDebInfo\bench_internal.exe
- build\src\RelWithDebInfo\bench.exe

2
vendor/secp256k1/repo/.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
src/precomputed_ecmult.c linguist-generated
src/precomputed_ecmult_gen.c linguist-generated

63
vendor/secp256k1/repo/.gitignore vendored Normal file
View file

@ -0,0 +1,63 @@
bench
bench_ecmult
bench_internal
noverify_tests
tests
exhaustive_tests
precompute_ecmult_gen
precompute_ecmult
ctime_tests
ecdh_example
ecdsa_example
schnorr_example
*.exe
*.so
*.a
*.csv
*.log
*.trs
*.sage.py
Makefile
configure
.libs/
Makefile.in
aclocal.m4
autom4te.cache/
config.log
config.status
conftest*
*.tar.gz
*.la
libtool
.deps/
.dirstamp
*.lo
*.o
*~
coverage/
coverage.html
coverage.*.html
*.gcda
*.gcno
*.gcov
build-aux/ar-lib
build-aux/config.guess
build-aux/config.sub
build-aux/depcomp
build-aux/install-sh
build-aux/ltmain.sh
build-aux/m4/libtool.m4
build-aux/m4/lt~obsolete.m4
build-aux/m4/ltoptions.m4
build-aux/m4/ltsugar.m4
build-aux/m4/ltversion.m4
build-aux/missing
build-aux/compile
build-aux/test-driver
libsecp256k1.pc
# Default CMake build directory.
/build

61
vendor/secp256k1/repo/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,61 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.3.0] - 2023-03-08
#### Added
- Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported.
- Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory.
- Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target.
#### Fixed
- Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning.
#### Changed
- Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.)
- Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization.
#### Removed
- Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags).
#### ABI Compatibility
Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is *not* compatible with previous versions.
## [0.2.0] - 2022-12-12
#### Added
- Added usage examples for common use cases in a new `examples/` directory.
- Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`.
- Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms.
#### Changed
- Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`.
- The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API.
#### Deprecated
- Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead.
- Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`.
- Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`.
#### ABI Compatibility
Since this is the first release, we do not compare application binary interfaces.
However, there are earlier unreleased versions of libsecp256k1 that are *not* ABI compatible with this version.
## [0.1.0] - 2013-03-05 to 2021-12-25
This version was in fact never released.
The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6).
Therefore, this version number does not uniquely identify a set of source files.
[unreleased]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.0...HEAD
[0.3.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/bitcoin-core/secp256k1/compare/423b6d19d373f1224fd671a982584d7e7900bc93..v0.2.0
[0.1.0]: https://github.com/bitcoin-core/secp256k1/commit/423b6d19d373f1224fd671a982584d7e7900bc93

302
vendor/secp256k1/repo/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,302 @@
cmake_minimum_required(VERSION 3.1)
if(CMAKE_VERSION VERSION_GREATER 3.14)
# MSVC runtime library flags are selected by the CMAKE_MSVC_RUNTIME_LIBRARY abstraction.
cmake_policy(SET CMP0091 NEW)
# MSVC warning flags are not in CMAKE_<LANG>_FLAGS by default.
cmake_policy(SET CMP0092 NEW)
endif()
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
# the API. All changes in experimental modules are treated as
# backwards-compatible and therefore at most increase the minor version.
project(libsecp256k1 VERSION 0.3.0 LANGUAGES C)
# The library version is based on libtool versioning of the ABI. The set of
# rules for updating the version can be found here:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
set(${PROJECT_NAME}_LIB_VERSION_CURRENT 2)
set(${PROJECT_NAME}_LIB_VERSION_REVISION 0)
set(${PROJECT_NAME}_LIB_VERSION_AGE 0)
set(CMAKE_C_STANDARD 90)
set(CMAKE_C_EXTENSIONS OFF)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# We do not use CMake's BUILD_SHARED_LIBS option.
option(SECP256K1_BUILD_SHARED "Build shared library." ON)
option(SECP256K1_BUILD_STATIC "Build static library." ON)
if(NOT SECP256K1_BUILD_SHARED AND NOT SECP256K1_BUILD_STATIC)
message(FATAL_ERROR "At least one of SECP256K1_BUILD_SHARED and SECP256K1_BUILD_STATIC must be enabled.")
endif()
option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON)
if(SECP256K1_ENABLE_MODULE_ECDH)
add_definitions(-DENABLE_MODULE_ECDH=1)
endif()
option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF)
if(SECP256K1_ENABLE_MODULE_RECOVERY)
add_definitions(-DENABLE_MODULE_RECOVERY=1)
endif()
option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON)
option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON)
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON)
add_definitions(-DENABLE_MODULE_SCHNORRSIG=1)
endif()
if(SECP256K1_ENABLE_MODULE_EXTRAKEYS)
add_definitions(-DENABLE_MODULE_EXTRAKEYS=1)
endif()
option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF)
if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS)
add_definitions(-DUSE_EXTERNAL_DEFAULT_CALLBACKS=1)
endif()
set(SECP256K1_ECMULT_WINDOW_SIZE "AUTO" CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. \"AUTO\" is a reasonable setting for desktop machines (currently 15). [default=AUTO]")
set_property(CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS "AUTO" 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24)
include(CheckStringOptionValue)
check_string_option_value(SECP256K1_ECMULT_WINDOW_SIZE)
if(SECP256K1_ECMULT_WINDOW_SIZE STREQUAL "AUTO")
set(SECP256K1_ECMULT_WINDOW_SIZE 15)
endif()
add_definitions(-DECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE})
set(SECP256K1_ECMULT_GEN_PREC_BITS "AUTO" CACHE STRING "Precision bits to tune the precomputed table size for signing, specified as integer 2, 4 or 8. \"AUTO\" is a reasonable setting for desktop machines (currently 4). [default=AUTO]")
set_property(CACHE SECP256K1_ECMULT_GEN_PREC_BITS PROPERTY STRINGS "AUTO" 2 4 8)
check_string_option_value(SECP256K1_ECMULT_GEN_PREC_BITS)
if(SECP256K1_ECMULT_GEN_PREC_BITS STREQUAL "AUTO")
set(SECP256K1_ECMULT_GEN_PREC_BITS 4)
endif()
add_definitions(-DECMULT_GEN_PREC_BITS=${SECP256K1_ECMULT_GEN_PREC_BITS})
set(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \"widemul\" setting. Legal values are: \"OFF\", \"int128_struct\", \"int128\" or \"int64\". [default=OFF]")
set_property(CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64")
check_string_option_value(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
string(TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value)
add_definitions(-DUSE_FORCE_WIDEMUL_${widemul_upper_value}=1)
endif()
mark_as_advanced(FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly optimizations to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm\" (experimental). [default=AUTO]")
set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm")
check_string_option_value(SECP256K1_ASM)
if(SECP256K1_ASM STREQUAL "arm")
enable_language(ASM)
add_definitions(-DUSE_EXTERNAL_ASM=1)
elseif(SECP256K1_ASM)
include(Check64bitAssembly)
check_64bit_assembly()
if(HAS_64BIT_ASM)
set(SECP256K1_ASM "x86_64")
add_definitions(-DUSE_ASM_X86_64=1)
elseif(SECP256K1_ASM STREQUAL "AUTO")
set(SECP256K1_ASM "OFF")
else()
message(FATAL_ERROR "x86_64 assembly optimization requested but not available.")
endif()
endif()
option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF)
if(NOT SECP256K1_EXPERIMENTAL)
if(SECP256K1_ASM STREQUAL "arm")
message(FATAL_ERROR "ARM assembly optimization is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.")
endif()
endif()
set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]")
set_property(CACHE SECP256K1_VALGRIND PROPERTY STRINGS "AUTO" "OFF" "ON")
check_string_option_value(SECP256K1_VALGRIND)
if(SECP256K1_VALGRIND)
find_package(Valgrind MODULE)
if(Valgrind_FOUND)
set(SECP256K1_VALGRIND ON)
include_directories(${Valgrind_INCLUDE_DIR})
add_definitions(-DVALGRIND)
elseif(SECP256K1_VALGRIND STREQUAL "AUTO")
set(SECP256K1_VALGRIND OFF)
else()
message(FATAL_ERROR "Valgrind support requested but valgrind/memcheck.h header not available.")
endif()
endif()
option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." ON)
option(SECP256K1_BUILD_TESTS "Build tests." ON)
option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." ON)
option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." ${SECP256K1_VALGRIND})
option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF)
# Redefine configuration flags.
# We leave assertions on, because they are only used in the examples, and we want them always on there.
if(MSVC)
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
else()
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
# Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.)
string(REGEX REPLACE "-O3[ \t\r\n]*" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
endif()
# Define custom "Coverage" build type.
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O0 -DCOVERAGE=1 --coverage -Wno-unused-parameter" CACHE STRING
"Flags used by the C compiler during \"Coverage\" builds."
FORCE
)
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
"Flags used for linking binaries during \"Coverage\" builds."
FORCE
)
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING
"Flags used by the shared libraries linker during \"Coverage\" builds."
FORCE
)
mark_as_advanced(
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE
)
if(CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage")
endif()
get_property(cached_cmake_build_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE)
if(cached_cmake_build_type)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "RelWithDebInfo" "Release" "Debug" "MinSizeRel" "Coverage"
)
endif()
set(default_build_type "RelWithDebInfo")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to \"${default_build_type}\" as none was specified")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
endif()
include(TryAddCompileOption)
if(MSVC)
try_add_compile_option(/W2)
try_add_compile_option(/wd4146)
else()
try_add_compile_option(-pedantic)
try_add_compile_option(-Wall)
try_add_compile_option(-Wcast-align)
try_add_compile_option(-Wcast-align=strict)
try_add_compile_option(-Wconditional-uninitialized)
try_add_compile_option(-Wextra)
try_add_compile_option(-Wnested-externs)
try_add_compile_option(-Wno-long-long)
try_add_compile_option(-Wno-overlength-strings)
try_add_compile_option(-Wno-unused-function)
try_add_compile_option(-Wreserved-identifier)
try_add_compile_option(-Wshadow)
try_add_compile_option(-Wstrict-prototypes)
try_add_compile_option(-Wundef)
endif()
if(CMAKE_VERSION VERSION_GREATER 3.2)
# Honor visibility properties for all target types.
# See: https://cmake.org/cmake/help/latest/policy/CMP0063.html
cmake_policy(SET CMP0063 NEW)
endif()
set(CMAKE_C_VISIBILITY_PRESET hidden)
# Ask CTest to create a "check" target (e.g., make check) as alias for the "test" target.
# CTEST_TEST_TARGET_ALIAS is not documented but supposed to be user-facing.
# See: https://gitlab.kitware.com/cmake/cmake/-/commit/816c9d1aa1f2b42d40c81a991b68c96eb12b6d2
set(CTEST_TEST_TARGET_ALIAS check)
include(CTest)
# We do not use CTest's BUILD_TESTING because a single toggle for all tests is too coarse for our needs.
mark_as_advanced(BUILD_TESTING)
if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUSTIVE_TESTS OR SECP256K1_BUILD_CTIME_TESTS OR SECP256K1_BUILD_EXAMPLES)
enable_testing()
endif()
add_subdirectory(src)
if(SECP256K1_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
message("\n")
message("secp256k1 configure summary")
message("===========================")
message("Build artifacts:")
message(" shared library ...................... ${SECP256K1_BUILD_SHARED}")
message(" static library ...................... ${SECP256K1_BUILD_STATIC}")
message("Optional modules:")
message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}")
message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}")
message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}")
message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}")
message("Parameters:")
message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}")
message(" ecmult gen precision bits ........... ${SECP256K1_ECMULT_GEN_PREC_BITS}")
message("Optional features:")
message(" assembly optimization ............... ${SECP256K1_ASM}")
message(" external callbacks .................. ${SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS}")
if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY)
message(" wide multiplication (test-only) ..... ${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}")
endif()
message("Optional binaries:")
message(" benchmark ........................... ${SECP256K1_BUILD_BENCHMARK}")
message(" noverify_tests ...................... ${SECP256K1_BUILD_TESTS}")
set(tests_status "${SECP256K1_BUILD_TESTS}")
if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
set(tests_status OFF)
endif()
message(" tests ............................... ${tests_status}")
message(" exhaustive tests .................... ${SECP256K1_BUILD_EXHAUSTIVE_TESTS}")
message(" ctime_tests ......................... ${SECP256K1_BUILD_CTIME_TESTS}")
message(" examples ............................ ${SECP256K1_BUILD_EXAMPLES}")
message("")
if(CMAKE_CROSSCOMPILING)
set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}")
else()
set(cross_status "FALSE")
endif()
message("Cross compiling ....................... ${cross_status}")
message("Valgrind .............................. ${SECP256K1_VALGRIND}")
get_directory_property(definitions COMPILE_DEFINITIONS)
string(REPLACE ";" " " definitions "${definitions}")
message("Preprocessor defined macros ........... ${definitions}")
message("C compiler ............................ ${CMAKE_C_COMPILER}")
message("CFLAGS ................................ ${CMAKE_C_FLAGS}")
get_directory_property(compile_options COMPILE_OPTIONS)
string(REPLACE ";" " " compile_options "${compile_options}")
message("Compile options ....................... " ${compile_options})
if(DEFINED CMAKE_BUILD_TYPE)
message("Build type:")
message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}")
string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}")
else()
message("Available configurations .............. ${CMAKE_CONFIGURATION_TYPES}")
message("RelWithDebInfo configuration:")
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
message("Debug configuration:")
message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}")
message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}")
endif()
message("\n")
if(SECP256K1_EXPERIMENTAL)
message(
" ******\n"
" WARNING: experimental build\n"
" Experimental features do not have stable APIs or properties, and may not be safe for production use.\n"
" ******\n"
)
endif()

19
vendor/secp256k1/repo/COPYING vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2013 Pieter Wuille
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

249
vendor/secp256k1/repo/Makefile.am vendored Normal file
View file

@ -0,0 +1,249 @@
.PHONY: clean-precomp precomp
ACLOCAL_AMFLAGS = -I build-aux/m4
# AM_CFLAGS will be automatically prepended to CFLAGS by Automake when compiling some foo
# which does not have an explicit foo_CFLAGS variable set.
AM_CFLAGS = $(SECP_CFLAGS)
lib_LTLIBRARIES = libsecp256k1.la
include_HEADERS = include/secp256k1.h
include_HEADERS += include/secp256k1_preallocated.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h
noinst_HEADERS += src/scalar_8x32.h
noinst_HEADERS += src/scalar_low.h
noinst_HEADERS += src/scalar_impl.h
noinst_HEADERS += src/scalar_4x64_impl.h
noinst_HEADERS += src/scalar_8x32_impl.h
noinst_HEADERS += src/scalar_low_impl.h
noinst_HEADERS += src/group.h
noinst_HEADERS += src/group_impl.h
noinst_HEADERS += src/ecdsa.h
noinst_HEADERS += src/ecdsa_impl.h
noinst_HEADERS += src/eckey.h
noinst_HEADERS += src/eckey_impl.h
noinst_HEADERS += src/ecmult.h
noinst_HEADERS += src/ecmult_impl.h
noinst_HEADERS += src/ecmult_compute_table.h
noinst_HEADERS += src/ecmult_compute_table_impl.h
noinst_HEADERS += src/ecmult_const.h
noinst_HEADERS += src/ecmult_const_impl.h
noinst_HEADERS += src/ecmult_gen.h
noinst_HEADERS += src/ecmult_gen_impl.h
noinst_HEADERS += src/ecmult_gen_compute_table.h
noinst_HEADERS += src/ecmult_gen_compute_table_impl.h
noinst_HEADERS += src/field_10x26.h
noinst_HEADERS += src/field_10x26_impl.h
noinst_HEADERS += src/field_5x52.h
noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/modinv32.h
noinst_HEADERS += src/modinv32_impl.h
noinst_HEADERS += src/modinv64.h
noinst_HEADERS += src/modinv64_impl.h
noinst_HEADERS += src/precomputed_ecmult.h
noinst_HEADERS += src/precomputed_ecmult_gen.h
noinst_HEADERS += src/assumptions.h
noinst_HEADERS += src/checkmem.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/int128.h
noinst_HEADERS += src/int128_impl.h
noinst_HEADERS += src/int128_native.h
noinst_HEADERS += src/int128_native_impl.h
noinst_HEADERS += src/int128_struct.h
noinst_HEADERS += src/int128_struct_impl.h
noinst_HEADERS += src/scratch.h
noinst_HEADERS += src/scratch_impl.h
noinst_HEADERS += src/selftest.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
noinst_HEADERS += src/hash_impl.h
noinst_HEADERS += src/field.h
noinst_HEADERS += src/field_impl.h
noinst_HEADERS += src/bench.h
noinst_HEADERS += contrib/lax_der_parsing.h
noinst_HEADERS += contrib/lax_der_parsing.c
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
noinst_HEADERS += examples/examples_util.h
PRECOMPUTED_LIB = libsecp256k1_precomputed.la
noinst_LTLIBRARIES = $(PRECOMPUTED_LIB)
libsecp256k1_precomputed_la_SOURCES = src/precomputed_ecmult.c src/precomputed_ecmult_gen.c
# We need `-I$(top_srcdir)/src` in VPATH builds if libsecp256k1_precomputed_la_SOURCES have been recreated in the build tree.
# This helps users and packagers who insist on recreating the precomputed files (e.g., Gentoo).
libsecp256k1_precomputed_la_CPPFLAGS = -I$(top_srcdir)/src $(SECP_CONFIG_DEFINES)
if USE_EXTERNAL_ASM
COMMON_LIB = libsecp256k1_common.la
else
COMMON_LIB =
endif
noinst_LTLIBRARIES += $(COMMON_LIB)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc
if USE_EXTERNAL_ASM
if USE_ASM_ARM
libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s
endif
endif
libsecp256k1_la_SOURCES = src/secp256k1.c
libsecp256k1_la_CPPFLAGS = $(SECP_CONFIG_DEFINES)
libsecp256k1_la_LIBADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE)
noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench bench_internal bench_ecmult
bench_SOURCES = src/bench.c
bench_LDADD = libsecp256k1.la
bench_CPPFLAGS = $(SECP_CONFIG_DEFINES)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
bench_internal_CPPFLAGS = $(SECP_CONFIG_DEFINES)
bench_ecmult_SOURCES = src/bench_ecmult.c
bench_ecmult_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
bench_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES)
endif
TESTS =
if USE_TESTS
TESTS += noverify_tests
noinst_PROGRAMS += noverify_tests
noverify_tests_SOURCES = src/tests.c
noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
noverify_tests_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB)
noverify_tests_LDFLAGS = -static
if !ENABLE_COVERAGE
TESTS += tests
noinst_PROGRAMS += tests
tests_SOURCES = $(noverify_tests_SOURCES)
tests_CPPFLAGS = $(noverify_tests_CPPFLAGS) -DVERIFY
tests_LDADD = $(noverify_tests_LDADD)
tests_LDFLAGS = $(noverify_tests_LDFLAGS)
endif
endif
if USE_CTIME_TESTS
noinst_PROGRAMS += ctime_tests
ctime_tests_SOURCES = src/ctime_tests.c
ctime_tests_LDADD = libsecp256k1.la
ctime_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
endif
if USE_EXHAUSTIVE_TESTS
noinst_PROGRAMS += exhaustive_tests
exhaustive_tests_SOURCES = src/tests_exhaustive.c
exhaustive_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
if !ENABLE_COVERAGE
exhaustive_tests_CPPFLAGS += -DVERIFY
endif
# Note: do not include $(PRECOMPUTED_LIB) in exhaustive_tests (it uses runtime-generated tables).
exhaustive_tests_LDADD = $(COMMON_LIB)
exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests
endif
if USE_EXAMPLES
noinst_PROGRAMS += ecdsa_example
ecdsa_example_SOURCES = examples/ecdsa.c
ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include
ecdsa_example_LDADD = libsecp256k1.la
ecdsa_example_LDFLAGS = -static
if BUILD_WINDOWS
ecdsa_example_LDFLAGS += -lbcrypt
endif
TESTS += ecdsa_example
if ENABLE_MODULE_ECDH
noinst_PROGRAMS += ecdh_example
ecdh_example_SOURCES = examples/ecdh.c
ecdh_example_CPPFLAGS = -I$(top_srcdir)/include
ecdh_example_LDADD = libsecp256k1.la
ecdh_example_LDFLAGS = -static
if BUILD_WINDOWS
ecdh_example_LDFLAGS += -lbcrypt
endif
TESTS += ecdh_example
endif
if ENABLE_MODULE_SCHNORRSIG
noinst_PROGRAMS += schnorr_example
schnorr_example_SOURCES = examples/schnorr.c
schnorr_example_CPPFLAGS = -I$(top_srcdir)/include
schnorr_example_LDADD = libsecp256k1.la
schnorr_example_LDFLAGS = -static
if BUILD_WINDOWS
schnorr_example_LDFLAGS += -lbcrypt
endif
TESTS += schnorr_example
endif
endif
### Precomputed tables
EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen
CLEANFILES = $(EXTRA_PROGRAMS)
precompute_ecmult_SOURCES = src/precompute_ecmult.c
precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES)
precompute_ecmult_LDADD = $(COMMON_LIB)
precompute_ecmult_gen_SOURCES = src/precompute_ecmult_gen.c
precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES)
precompute_ecmult_gen_LDADD = $(COMMON_LIB)
# See Automake manual, Section "Errors with distclean".
# We don't list any dependencies for the prebuilt files here because
# otherwise make's decision whether to rebuild them (even in the first
# build by a normal user) depends on mtimes, and thus is very fragile.
# This means that rebuilds of the prebuilt files always need to be
# forced by deleting them, e.g., by invoking `make clean-precomp`.
src/precomputed_ecmult.c:
$(MAKE) $(AM_MAKEFLAGS) precompute_ecmult$(EXEEXT)
./precompute_ecmult$(EXEEXT)
src/precomputed_ecmult_gen.c:
$(MAKE) $(AM_MAKEFLAGS) precompute_ecmult_gen$(EXEEXT)
./precompute_ecmult_gen$(EXEEXT)
PRECOMP = src/precomputed_ecmult_gen.c src/precomputed_ecmult.c
precomp: $(PRECOMP)
# Ensure the prebuilt files will be build first (only if they don't exist,
# e.g., after `make maintainer-clean`).
BUILT_SOURCES = $(PRECOMP)
maintainer-clean-local: clean-precomp
clean-precomp:
rm -f $(PRECOMP)
EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md
EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md
EXTRA_DIST += examples/EXAMPLES_COPYING
EXTRA_DIST += sage/gen_exhaustive_groups.sage
EXTRA_DIST += sage/gen_split_lambda_constants.sage
EXTRA_DIST += sage/group_prover.sage
EXTRA_DIST += sage/prove_group_implementations.sage
EXTRA_DIST += sage/secp256k1_params.sage
EXTRA_DIST += sage/weierstrass_prover.sage
if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
if ENABLE_MODULE_EXTRAKEYS
include src/modules/extrakeys/Makefile.am.include
endif
if ENABLE_MODULE_SCHNORRSIG
include src/modules/schnorrsig/Makefile.am.include
endif

157
vendor/secp256k1/repo/README.md vendored Normal file
View file

@ -0,0 +1,157 @@
libsecp256k1
============
[![Build Status](https://api.cirrus-ci.com/github/bitcoin-core/secp256k1.svg?branch=master)](https://cirrus-ci.com/github/bitcoin-core/secp256k1)
![Dependencies: None](https://img.shields.io/badge/dependencies-none-success)
[![irc.libera.chat #secp256k1](https://img.shields.io/badge/irc.libera.chat-%23secp256k1-success)](https://web.libera.chat/#secp256k1)
Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose.
Features:
* secp256k1 ECDSA signing/verification and key generation.
* Additive and multiplicative tweaking of secret/public keys.
* Serialization/parsing of secret keys, public keys, signatures.
* Constant time, constant memory access signing and public key generation.
* Derandomized ECDSA (via RFC6979 or with a caller provided function.)
* Very efficient implementation.
* Suitable for embedded systems.
* No runtime dependencies.
* Optional module for public key recovery.
* Optional module for ECDH key exchange.
* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
Implementation details
----------------------
* General
* No runtime heap allocation.
* Extensive testing infrastructure.
* Structured to facilitate review and analysis.
* Intended to be portable to any system with a C89 compiler and uint64_t support.
* No use of floating types.
* Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.")
* Field operations
* Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1).
* Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys).
* Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
* This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community.
* Scalar operations
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
* Using 4 64-bit limbs (relying on __int128 support in the compiler).
* Using 8 32-bit limbs.
* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman).
* Group operations
* Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7).
* Use addition between points in Jacobian and affine coordinates where possible.
* Use a unified addition/doubling formula where necessary to avoid data-dependent branches.
* Point/x comparison without a field inversion by comparison in the Jacobian coordinate space.
* Point multiplication for verification (a*P + b*G).
* Use wNAF notation for point multiplicands.
* Use a much larger window for multiples of G, using precomputed multiples.
* Use Shamir's trick to do the multiplication with the public key and the generator simultaneously.
* Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
* Point multiplication for signing
* Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions.
* Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains)
* Access the table with branch-free conditional moves so memory access is uniform.
* No data-dependent branches
* Optional runtime blinding which attempts to frustrate differential power analysis.
* The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally.
Building with Autotools
-----------------------
$ ./autogen.sh
$ ./configure
$ make
$ make check # run the test suite
$ sudo make install # optional
To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags.
Building with CMake (experimental)
----------------------------------
To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree.
### Building on POSIX systems
$ mkdir build && cd build
$ cmake ..
$ make
$ make check # run the test suite
$ sudo make install # optional
To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags.
### Cross compiling
To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory.
For example, to cross compile for Windows:
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake
To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set):
$ cmake .. -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28
### Building on Windows
To build on Windows with Visual Studio, a proper [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) must be specified for a new build tree.
The following example assumes using of Visual Studio 2022 and CMake v3.21+.
In "Developer Command Prompt for VS 2022":
>cmake -G "Visual Studio 17 2022" -A x64 -S . -B build
>cmake --build build --config RelWithDebInfo
Usage examples
-----------
Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`.
* [ECDSA example](examples/ecdsa.c)
* [Schnorr signatures example](examples/schnorr.c)
* [Deriving a shared secret (ECDH) example](examples/ecdh.c)
To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`.
Test coverage
-----------
This library aims to have full coverage of the reachable lines and branches.
To create a test coverage report, configure with `--enable-coverage` (use of GCC is necessary):
$ ./configure --enable-coverage
Run the tests:
$ make check
To create a report, `gcovr` is recommended, as it includes branch coverage reporting:
$ gcovr --exclude 'src/bench*' --print-summary
To create a HTML report with coloured and annotated source code:
$ mkdir -p coverage
$ gcovr --exclude 'src/bench*' --html --html-details -o coverage/coverage.html
Benchmark
------------
If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build.
To print the benchmark result to the command line:
$ ./bench_name
To create a CSV file for the benchmark result :
$ ./bench_name | sed '2d;s/ \{1,\}//g' > bench_name.csv
Reporting a vulnerability
------------
See [SECURITY.md](SECURITY.md)

15
vendor/secp256k1/repo/SECURITY.md vendored Normal file
View file

@ -0,0 +1,15 @@
# Security Policy
## Reporting a Vulnerability
To report security issues send an email to secp256k1-security@bitcoincore.org (not for support).
The following keys may be used to communicate sensitive information to developers:
| Name | Fingerprint |
|------|-------------|
| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 |
| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 |
| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
You can import a key by running the following command with that individuals fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.

3
vendor/secp256k1/repo/autogen.sh vendored Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
set -e
autoreconf -if --warnings=all

View file

@ -0,0 +1,55 @@
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
AC_MSG_CHECKING(for x86_64 assembly availability)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#include <stdint.h>]],[[
uint64_t a = 11, tmp;
__asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
]])],[has_64bit_asm=yes],[has_64bit_asm=no])
AC_MSG_RESULT([$has_64bit_asm])
])
AC_DEFUN([SECP_VALGRIND_CHECK],[
AC_MSG_CHECKING([for valgrind support])
if test x"$has_valgrind" != x"yes"; then
CPPFLAGS_TEMP="$CPPFLAGS"
CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <valgrind/memcheck.h>
]], [[
#if defined(NVALGRIND)
# error "Valgrind does not support this platform."
#endif
]])], [has_valgrind=yes])
fi
AC_MSG_RESULT($has_valgrind)
])
dnl SECP_TRY_APPEND_CFLAGS(flags, VAR)
dnl Append flags to VAR if CC accepts them.
AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [
AC_MSG_CHECKING([if ${CC} supports $1])
SECP_TRY_APPEND_CFLAGS_saved_CFLAGS="$CFLAGS"
CFLAGS="$1 $CFLAGS"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [flag_works=yes], [flag_works=no])
AC_MSG_RESULT($flag_works)
CFLAGS="$SECP_TRY_APPEND_CFLAGS_saved_CFLAGS"
if test x"$flag_works" = x"yes"; then
$2="$$2 $1"
fi
unset flag_works
AC_SUBST($2)
])
dnl SECP_SET_DEFAULT(VAR, default, default-dev-mode)
dnl Set VAR to default or default-dev-mode, depending on whether dev mode is enabled
AC_DEFUN([SECP_SET_DEFAULT], [
if test "${enable_dev_mode+set}" != set; then
AC_MSG_ERROR([[Set enable_dev_mode before calling SECP_SET_DEFAULT]])
fi
if test x"$enable_dev_mode" = x"yes"; then
$1="$3"
else
$1="$2"
fi
])

118
vendor/secp256k1/repo/ci/cirrus.sh vendored Executable file
View file

@ -0,0 +1,118 @@
#!/bin/sh
set -eux
export LC_ALL=C
# Print relevant CI environment to allow reproducing the job outside of CI.
print_environment() {
# Turn off -x because it messes up the output
set +x
# There are many ways to print variable names and their content. This one
# does not rely on bash.
for var in WERROR_CFLAGS MAKEFLAGS BUILD \
ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
EXPERIMENTAL ECDH RECOVERY SCHNORRSIG \
SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
EXAMPLES \
HOST WRAPPER_CMD \
CC CFLAGS CPPFLAGS AR NM
do
eval "isset=\${$var+x}"
if [ -n "$isset" ]; then
eval "val=\${$var}"
# shellcheck disable=SC2154
printf '%s="%s" ' "$var" "$val"
fi
done
echo "$0"
set -x
}
print_environment
# Start persistent wineserver if necessary.
# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously.
case "$WRAPPER_CMD" in
*wine*)
# Make sure to shutdown wineserver whenever we exit.
trap "wineserver -k || true" EXIT INT HUP
# This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards.
wineserver -p && wine hh.exe
;;
esac
env >> test_env.log
if [ -n "${CC+x}" ]; then
# The MSVC compiler "cl" doesn't understand "-v"
$CC -v || true
fi
if [ "$WITH_VALGRIND" = "yes" ]; then
valgrind --version
fi
if [ -n "$WRAPPER_CMD" ]; then
$WRAPPER_CMD --version
fi
./autogen.sh
./configure \
--enable-experimental="$EXPERIMENTAL" \
--with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
--with-ecmult-window="$ECMULTWINDOW" \
--with-ecmult-gen-precision="$ECMULTGENPRECISION" \
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
--enable-module-schnorrsig="$SCHNORRSIG" \
--enable-examples="$EXAMPLES" \
--enable-ctime-tests="$CTIMETESTS" \
--with-valgrind="$WITH_VALGRIND" \
--host="$HOST" $EXTRAFLAGS
# We have set "-j<n>" in MAKEFLAGS.
make
# Print information about binaries so that we can see that the architecture is correct
file *tests* || true
file bench* || true
file .libs/* || true
# This tells `make check` to wrap test invocations.
export LOG_COMPILER="$WRAPPER_CMD"
make "$BUILD"
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
EXEC='./libtool --mode=execute'
if [ -n "$WRAPPER_CMD" ]
then
EXEC="$EXEC $WRAPPER_CMD"
fi
if [ "$BENCH" = "yes" ]
then
{
$EXEC ./bench_ecmult
$EXEC ./bench_internal
$EXEC ./bench
} >> bench.log 2>&1
fi
if [ "$CTIMETESTS" = "yes" ]
then
if [ "$WITH_VALGRIND" = "yes" ]; then
./libtool --mode=execute valgrind --error-exitcode=42 ./ctime_tests > ctime_tests.log 2>&1
else
$EXEC ./ctime_tests > ctime_tests.log 2>&1
fi
fi
# Rebuild precomputed files (if not cross-compiling).
if [ -z "$HOST" ]
then
make clean-precomp
make precomp
fi
# Check that no repo files have been modified by the build.
# (This fails for example if the precomp files need to be updated in the repo.)
git diff --exit-code

View file

@ -0,0 +1,37 @@
FROM debian:stable
RUN dpkg --add-architecture i386 && \
dpkg --add-architecture s390x && \
dpkg --add-architecture armhf && \
dpkg --add-architecture arm64 && \
dpkg --add-architecture ppc64el
# dkpg-dev: to make pkg-config work in cross-builds
# llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces
RUN apt-get update && apt-get install --no-install-recommends -y \
git ca-certificates \
make automake libtool pkg-config dpkg-dev valgrind qemu-user \
gcc clang llvm libc6-dbg \
g++ \
gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan6:i386 \
gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
gcc-mingw-w64-x86-64-win32 wine64 wine \
gcc-mingw-w64-i686-win32 wine32 \
sagemath
WORKDIR /root
# The "wine" package provides a convience wrapper that we need
RUN apt-get update && apt-get install --no-install-recommends -y \
git ca-certificates wine64 wine python3-simplejson python3-six msitools winbind procps && \
git clone https://github.com/mstorsjo/msvc-wine && \
mkdir /opt/msvc && \
python3 msvc-wine/vsdownload.py --accept-license --dest /opt/msvc Microsoft.VisualStudio.Workload.VCTools && \
msvc-wine/install.sh /opt/msvc
# Initialize the wine environment. Wait until the wineserver process has
# exited before closing the session, to avoid corrupting the wine prefix.
RUN wine64 wineboot --init && \
while (ps -A | grep wineserver) > /dev/null; do sleep 1; done

View file

@ -0,0 +1,14 @@
include(CheckCSourceCompiles)
function(check_64bit_assembly)
check_c_source_compiles("
#include <stdint.h>
int main()
{
uint64_t a = 11, tmp;
__asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\");
}
" HAS_64BIT_ASM)
set(HAS_64BIT_ASM ${HAS_64BIT_ASM} PARENT_SCOPE)
endfunction()

View file

@ -0,0 +1,12 @@
function(check_string_option_value option)
get_property(expected_values CACHE ${option} PROPERTY STRINGS)
if(expected_values)
foreach(value IN LISTS expected_values)
if(value STREQUAL "${${option}}")
return()
endif()
endforeach()
message(FATAL_ERROR "${option} value is \"${${option}}\", but must be one of ${expected_values}.")
endif()
message(AUTHOR_WARNING "The STRINGS property must be set before invoking `check_string_option_value' function.")
endfunction()

View file

@ -0,0 +1,41 @@
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
find_program(BREW_COMMAND brew)
execute_process(
COMMAND ${BREW_COMMAND} --prefix valgrind
OUTPUT_VARIABLE valgrind_brew_prefix
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
set(hints_paths)
if(valgrind_brew_prefix)
set(hints_paths ${valgrind_brew_prefix}/include)
endif()
find_path(Valgrind_INCLUDE_DIR
NAMES valgrind/memcheck.h
HINTS ${hints_paths}
)
if(Valgrind_INCLUDE_DIR)
include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${Valgrind_INCLUDE_DIR})
check_c_source_compiles("
#include <valgrind/memcheck.h>
#if defined(NVALGRIND)
# error \"Valgrind does not support this platform.\"
#endif
int main() {}
" Valgrind_WORKS)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Valgrind
REQUIRED_VARS Valgrind_INCLUDE_DIR Valgrind_WORKS
)
mark_as_advanced(
Valgrind_INCLUDE_DIR
)

View file

@ -0,0 +1,23 @@
include(CheckCCompilerFlag)
function(try_add_compile_option option)
string(MAKE_C_IDENTIFIER ${option} result)
string(TOUPPER ${result} result)
set(result "C_SUPPORTS${result}")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
if(NOT MSVC)
set(CMAKE_REQUIRED_FLAGS "-Werror")
endif()
check_c_compiler_flag(${option} ${result})
if(${result})
get_property(compile_options
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
PROPERTY COMPILE_OPTIONS
)
list(APPEND compile_options "${option}")
set_property(
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
PROPERTY COMPILE_OPTIONS "${compile_options}"
)
endif()
endfunction()

View file

@ -0,0 +1,3 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)

View file

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
check_required_components(@PROJECT_NAME@)

View file

@ -0,0 +1,3 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)

460
vendor/secp256k1/repo/configure.ac vendored Normal file
View file

@ -0,0 +1,460 @@
AC_PREREQ([2.60])
# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
# the API. All changes in experimental modules are treated as
# backwards-compatible and therefore at most increase the minor version.
define(_PKG_VERSION_MAJOR, 0)
define(_PKG_VERSION_MINOR, 3)
define(_PKG_VERSION_PATCH, 0)
define(_PKG_VERSION_IS_RELEASE, true)
# The library version is based on libtool versioning of the ABI. The set of
# rules for updating the version can be found here:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
# All changes in experimental modules are treated as if they don't affect the
# interface and therefore only increase the revision.
define(_LIB_VERSION_CURRENT, 2)
define(_LIB_VERSION_REVISION, 0)
define(_LIB_VERSION_AGE, 0)
AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([build-aux/m4])
AC_CANONICAL_HOST
# Require Automake 1.11.2 for AM_PROG_AR
AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects])
# Make the compilation flags quiet unless V=1 is used.
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_PROG_CC
AM_PROG_AS
AM_PROG_AR
# Clear some cache variables as a workaround for a bug that appears due to a bad
# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe.
# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421
AS_UNSET(ac_cv_prog_AR)
AS_UNSET(ac_cv_prog_ac_ct_AR)
LT_INIT([win32-dll])
build_windows=no
case $host_os in
*darwin*)
if test x$cross_compiling != xyes; then
AC_CHECK_PROG([BREW], brew, brew)
if test x$BREW = xbrew; then
# These Homebrew packages may be keg-only, meaning that they won't be found
# in expected paths because they may conflict with system files. Ask
# Homebrew where each one is located, then adjust paths accordingly.
if $BREW list --versions valgrind >/dev/null; then
valgrind_prefix=$($BREW --prefix valgrind 2>/dev/null)
VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
fi
else
AC_CHECK_PROG([PORT], port, port)
# If homebrew isn't installed and macports is, add the macports default paths
# as a last resort.
if test x$PORT = xport; then
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
fi
fi
fi
;;
cygwin*|mingw*)
build_windows=yes
;;
esac
# Try if some desirable compiler flags are supported and append them to SECP_CFLAGS.
#
# These are our own flags, so we append them to our own SECP_CFLAGS variable (instead of CFLAGS) as
# recommended in the automake manual (Section "Flag Variables Ordering"). CFLAGS belongs to the user
# and we are not supposed to touch it. In the Makefile, we will need to ensure that SECP_CFLAGS
# is prepended to CFLAGS when invoking the compiler so that the user always has the last word (flag).
#
# Another advantage of not touching CFLAGS is that the contents of CFLAGS will be picked up by
# libtool for compiling helper executables. For example, when compiling for Windows, libtool will
# generate entire wrapper executables (instead of simple wrapper scripts as on Unix) to ensure
# proper operation of uninstalled programs linked by libtool against the uninstalled shared library.
# These executables are compiled from C source file for which our flags may not be appropriate,
# e.g., -std=c89 flag has lead to undesirable warnings in the past.
#
# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues.
AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
# GCC and compatible (incl. clang)
if test "x$GCC" = "xyes"; then
# Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will
# not error out if it gets unknown warning flags and the checks here will always succeed
# no matter if clang knows the flag or not.
SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS)
SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only
SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0
CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
fi
# MSVC
# Assume MSVC if we're building for Windows but not with GCC or compatible;
# libtool makes the same assumption internally.
# Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path.
if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then
SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned"
SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files
# We pass -ignore:4217 to the MSVC linker to suppress warning 4217 when
# importing variables from a statically linked secp256k1.
# (See the libtool manual, section "Windows DLLs" for background.)
# Unfortunately, libtool tries to be too clever and strips "-Xlinker arg"
# into "arg", so this will be " -Xlinker -ignore:4217" after stripping.
LDFLAGS="-Xlinker -Xlinker -Xlinker -ignore:4217 $LDFLAGS"
fi
])
SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
###
### Define config arguments
###
# In dev mode, we enable all binaries and modules by default but individual options can still be overridden explicitly.
# Check for dev mode first because SECP_SET_DEFAULT needs enable_dev_mode set.
AC_ARG_ENABLE(dev_mode, [], [],
[enable_dev_mode=no])
AC_ARG_ENABLE(benchmark,
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [],
[SECP_SET_DEFAULT([enable_benchmark], [yes], [yes])])
AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [],
[SECP_SET_DEFAULT([enable_coverage], [no], [no])])
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [],
[SECP_SET_DEFAULT([enable_tests], [yes], [yes])])
AC_ARG_ENABLE(ctime_tests,
AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [],
[SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])])
AC_ARG_ENABLE(experimental,
AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [],
[SECP_SET_DEFAULT([enable_experimental], [no], [yes])])
AC_ARG_ENABLE(exhaustive_tests,
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [],
[SECP_SET_DEFAULT([enable_exhaustive_tests], [yes], [yes])])
AC_ARG_ENABLE(examples,
AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [],
[SECP_SET_DEFAULT([enable_examples], [no], [yes])])
AC_ARG_ENABLE(module_ecdh,
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_ecdh], [yes], [yes])])
AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [],
[SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])])
AC_ARG_ENABLE(module_extrakeys,
AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_extrakeys], [yes], [yes])])
AC_ARG_ENABLE(module_schnorrsig,
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [],
[SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])])
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
# Test-only override of the (autodetected by the C code) "widemul" setting.
# Legal values are:
# * int64 (for [u]int64_t),
# * int128 (for [unsigned] __int128),
# * int128_struct (for int128 implemented as a structure),
# * and auto (the default).
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
[The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
[A window size larger than 15 will require you delete the prebuilt precomputed_ecmult.c file so that it can be rebuilt.]
[For very large window sizes, use "make -j 1" to reduce memory use during compilation.]
["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
)],
[req_ecmult_window=$withval], [req_ecmult_window=auto])
AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision=2|4|8|auto],
[Precision bits to tune the precomputed table size for signing.]
[The size of the table is 32kB for 2 bits, 64kB for 4 bits, 512kB for 8 bits of precision.]
[A larger table size usually results in possible faster signing.]
["auto" is a reasonable setting for desktop machines (currently 4). [default=auto]]
)],
[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto],
[Build with extra checks for running inside Valgrind [default=auto]]
)],
[req_valgrind=$withval], [req_valgrind=auto])
###
### Handle config options (except for modules)
###
if test x"$req_valgrind" = x"no"; then
enable_valgrind=no
else
SECP_VALGRIND_CHECK
if test x"$has_valgrind" != x"yes"; then
if test x"$req_valgrind" = x"yes"; then
AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available])
fi
enable_valgrind=no
else
enable_valgrind=yes
fi
fi
if test x"$enable_ctime_tests" = x"auto"; then
enable_ctime_tests=$enable_valgrind
fi
if test x"$enable_coverage" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1"
SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS"
LDFLAGS="--coverage $LDFLAGS"
else
# Most likely the CFLAGS already contain -O2 because that is autoconf's default.
# We still add it here because passing it twice is not an issue, and handling
# this case would just add unnecessary complexity (see #896).
SECP_CFLAGS="-O2 $SECP_CFLAGS"
fi
if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
set_asm=x86_64
fi
if test x"$set_asm" = x; then
set_asm=no
fi
else
set_asm=$req_asm
case $set_asm in
x86_64)
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" != x"yes"; then
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
fi
;;
arm)
;;
no)
;;
*)
AC_MSG_ERROR([invalid assembly optimization selection])
;;
esac
fi
# Select assembly optimization
enable_external_asm=no
case $set_asm in
x86_64)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1"
;;
arm)
enable_external_asm=yes
;;
no)
;;
*)
AC_MSG_ERROR([invalid assembly optimizations])
;;
esac
if test x"$enable_external_asm" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_ASM=1"
fi
# Select wide multiplication implementation
case $set_widemul in
int128_struct)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128_STRUCT=1"
;;
int128)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128=1"
;;
int64)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT64=1"
;;
auto)
;;
*)
AC_MSG_ERROR([invalid wide multiplication implementation])
;;
esac
# Set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
else
set_ecmult_window=$req_ecmult_window
fi
error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"']
case $set_ecmult_window in
''|*[[!0-9]]*)
# no valid integer
AC_MSG_ERROR($error_window_size)
;;
*)
if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
# not in range
AC_MSG_ERROR($error_window_size)
fi
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_WINDOW_SIZE=$set_ecmult_window"
;;
esac
# Set ecmult gen precision
if test x"$req_ecmult_gen_precision" = x"auto"; then
set_ecmult_gen_precision=4
else
set_ecmult_gen_precision=$req_ecmult_gen_precision
fi
case $set_ecmult_gen_precision in
2|4|8)
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_GEN_PREC_BITS=$set_ecmult_gen_precision"
;;
*)
AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"'])
;;
esac
if test x"$enable_valgrind" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND"
fi
# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI).
# We don't want to set the user variable CFLAGS in CI because this would disable
# autoconf's logic for setting default CFLAGS, which we would like to test in CI.
SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
###
### Handle module options
###
if test x"$enable_module_ecdh" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1"
fi
if test x"$enable_module_recovery" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1"
fi
if test x"$enable_module_schnorrsig" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1"
enable_module_extrakeys=yes
fi
# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
# module to set enable_module_extrakeys=yes
if test x"$enable_module_extrakeys" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1"
fi
if test x"$enable_external_default_callbacks" = x"yes"; then
SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1"
fi
###
### Check for --enable-experimental if necessary
###
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([******])
else
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
fi
###
### Generate output
###
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(SECP_CFLAGS)
AC_SUBST(SECP_CONFIG_DEFINES)
AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"])
AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"])
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT)
AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION)
AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE)
AC_OUTPUT
echo
echo "Build Options:"
echo " with external callbacks = $enable_external_default_callbacks"
echo " with benchmarks = $enable_benchmark"
echo " with tests = $enable_tests"
echo " with ctime tests = $enable_ctime_tests"
echo " with coverage = $enable_coverage"
echo " with examples = $enable_examples"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo
echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
# Hide test-only options unless they're used.
if test x"$set_widemul" != xauto; then
echo " wide multiplication = $set_widemul"
fi
echo
echo " valgrind = $enable_valgrind"
echo " CC = $CC"
echo " CPPFLAGS = $CPPFLAGS"
echo " SECP_CFLAGS = $SECP_CFLAGS"
echo " CFLAGS = $CFLAGS"
echo " LDFLAGS = $LDFLAGS"

View file

@ -0,0 +1,148 @@
/***********************************************************************
* Copyright (c) 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#include <string.h>
#include "lax_der_parsing.h"
int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
unsigned char tmpsig[64] = {0};
int overflow = 0;
/* Hack to initialize sig with a correctly-parsed but invalid signature. */
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
/* Sequence tag byte */
if (pos == inputlen || input[pos] != 0x30) {
return 0;
}
pos++;
/* Sequence length bytes */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (lenbyte > inputlen - pos) {
return 0;
}
pos += lenbyte;
}
/* Integer tag byte for R */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for R */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
rlen = 0;
while (lenbyte > 0) {
rlen = (rlen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
rlen = lenbyte;
}
if (rlen > inputlen - pos) {
return 0;
}
rpos = pos;
pos += rlen;
/* Integer tag byte for S */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for S */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (lenbyte > inputlen - pos) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
slen = 0;
while (lenbyte > 0) {
slen = (slen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
slen = lenbyte;
}
if (slen > inputlen - pos) {
return 0;
}
spos = pos;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
rlen--;
rpos++;
}
/* Copy R value */
if (rlen > 32) {
overflow = 1;
} else if (rlen) {
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
}
/* Ignore leading zeroes in S */
while (slen > 0 && input[spos] == 0) {
slen--;
spos++;
}
/* Copy S value */
if (slen > 32) {
overflow = 1;
} else if (slen) {
memcpy(tmpsig + 64 - slen, input + spos, slen);
}
if (!overflow) {
overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
if (overflow) {
memset(tmpsig, 0, 64);
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
return 1;
}

View file

@ -0,0 +1,97 @@
/***********************************************************************
* Copyright (c) 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
* project and does not promise any stability in its API, functionality or
* presence. Projects which use this code should instead copy this header
* and its accompanying .c file directly into their codebase.
****/
/* This file defines a function that parses DER with various errors and
* violations. This is not a part of the library itself, because the allowed
* violations are chosen arbitrarily and do not follow or establish any
* standard.
*
* In many places it matters that different implementations do not only accept
* the same set of valid signatures, but also reject the same set of signatures.
* The only means to accomplish that is by strictly obeying a standard, and not
* accepting anything else.
*
* Nonetheless, sometimes there is a need for compatibility with systems that
* use signatures which do not strictly obey DER. The snippet below shows how
* certain violations are easily supported. You may need to adapt it.
*
* Do not use this for new systems. Use well-defined DER or compact signatures
* instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and
* secp256k1_ecdsa_signature_parse_compact).
*
* The supported violations are:
* - All numbers are parsed as nonnegative integers, even though X.609-0207
* section 8.3.3 specifies that integers are always encoded as two's
* complement.
* - Integers can have length 0, even though section 8.3.1 says they can't.
* - Integers with overly long padding are accepted, violation section
* 8.3.2.
* - 127-byte long length descriptors are accepted, even though section
* 8.1.3.5.c says that they are not.
* - Trailing garbage data inside or after the signature is ignored.
* - The length descriptor of the sequence is ignored.
*
* Compared to for example OpenSSL, many violations are NOT supported:
* - Using overly long tag descriptors for the sequence or integers inside,
* violating section 8.1.2.2.
* - Encoding primitive integers as constructed values, violating section
* 8.3.1.
*/
#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H
#define SECP256K1_CONTRIB_LAX_DER_PARSING_H
/* #include secp256k1.h only when it hasn't been included yet.
This enables this file to be #included directly in other project
files (such as tests.c) without the need to set an explicit -I flag,
which would be necessary to locate secp256k1.h. */
#ifndef SECP256K1_H
#include <secp256k1.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** Parse a signature in "lax DER" format
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input: a pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
* encoded numbers are out of range. In addition, it will accept signatures
* which violate the DER spec in various ways. Its purpose is to allow
* validation of the Bitcoin blockchain, which includes non-DER signatures
* from before the network rules were updated to enforce DER. Note that
* the set of supported violations is a strict subset of what OpenSSL will
* accept.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature validation with it is
* guaranteed to fail for every message and public key.
*/
int ecdsa_signature_parse_der_lax(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature* sig,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */

View file

@ -0,0 +1,112 @@
/***********************************************************************
* Copyright (c) 2014, 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
#include <string.h>
#include "lax_der_privatekey_parsing.h"
int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
const unsigned char *end = privkey + privkeylen;
int lenb = 0;
int len = 0;
memset(out32, 0, 32);
/* sequence header */
if (end < privkey+1 || *privkey != 0x30) {
return 0;
}
privkey++;
/* sequence length constructor */
if (end < privkey+1 || !(*privkey & 0x80)) {
return 0;
}
lenb = *privkey & ~0x80; privkey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
if (end < privkey+lenb) {
return 0;
}
/* sequence length */
len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
privkey += lenb;
if (end < privkey+len) {
return 0;
}
/* sequence element 0: version number (=1) */
if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
return 0;
}
privkey += 3;
/* sequence element 1: octet string, up to 32 bytes */
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
return 0;
}
if (privkey[1]) memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
}
return 1;
}
int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) {
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
*privkeylen = 0;
return 0;
}
if (compressed) {
static const unsigned char begin[] = {
0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = 33;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = 65;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
}
return 1;
}

View file

@ -0,0 +1,95 @@
/***********************************************************************
* Copyright (c) 2014, 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
* project and does not promise any stability in its API, functionality or
* presence. Projects which use this code should instead copy this header
* and its accompanying .c file directly into their codebase.
****/
/* This file contains code snippets that parse DER private keys with
* various errors and violations. This is not a part of the library
* itself, because the allowed violations are chosen arbitrarily and
* do not follow or establish any standard.
*
* It also contains code to serialize private keys in a compatible
* manner.
*
* These functions are meant for compatibility with applications
* that require BER encoded keys. When working with secp256k1-specific
* code, the simple 32-byte private keys normally used by the
* library are sufficient.
*/
#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H
#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H
/* #include secp256k1.h only when it hasn't been included yet.
This enables this file to be #included directly in other project
files (such as tests.c) without the need to set an explicit -I flag,
which would be necessary to locate secp256k1.h. */
#ifndef SECP256K1_H
#include <secp256k1.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** Export a private key in DER format.
*
* Returns: 1 if the private key was valid.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: privkey: pointer to an array for storing the private key in BER.
* Should have space for 279 bytes, and cannot be NULL.
* privkeylen: Pointer to an int where the length of the private key in
* privkey will be stored.
* In: seckey: pointer to a 32-byte secret key to export.
* compressed: 1 if the key should be exported in
* compressed format, 0 otherwise
*
* This function is purely meant for compatibility with applications that
* require BER encoded keys. When working with secp256k1-specific code, the
* simple 32-byte private keys are sufficient.
*
* Note that this function does not guarantee correct DER output. It is
* guaranteed to be parsable by secp256k1_ec_privkey_import_der
*/
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der(
const secp256k1_context* ctx,
unsigned char *privkey,
size_t *privkeylen,
const unsigned char *seckey,
int compressed
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Import a private key in DER format.
* Returns: 1 if a private key was extracted.
* Args: ctx: pointer to a context object (cannot be NULL).
* Out: seckey: pointer to a 32-byte array for storing the private key.
* (cannot be NULL).
* In: privkey: pointer to a private key in DER format (cannot be NULL).
* privkeylen: length of the DER private key pointed to be privkey.
*
* This function will accept more than just strict DER, and even allow some BER
* violations. The public key stored inside the DER-encoded private key is not
* verified for correctness, nor are the curve parameters. Use this function
* only if you know in advance it is supposed to contain a secp256k1 private
* key.
*/
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *privkey,
size_t privkeylen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */

View file

@ -0,0 +1,52 @@
# Release Process
This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`.
We distinguish between two types of releases: *regular* and *maintenance* releases.
Regular releases are releases of a new major or minor version as well as patches of the most recent release.
Maintenance releases, on the other hand, are required for patches of older releases.
You should coordinate with the other maintainers on the release date, if possible.
This date will be part of the release entry in [CHANGELOG.md](../CHANGELOG.md) and it should match the dates of the remaining steps in the release process (including the date of the tag and the GitHub release).
It is best if the maintainers are present during the release, so they can help ensure that the process is followed correctly and, in the case of a regular release, they are aware that they should not modify the master branch between merging the PR in step 1 and the PR in step 3.
This process also assumes that there will be no minor releases for old major releases.
## Regular release
1. Open a PR to the master branch with a commit (using message `"release: prepare for $MAJOR.$MINOR.$PATCH"`, for example) that
* finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) (make sure to include an entry for `### ABI Compatibility`) and
* updates `_PKG_VERSION_*`, `_LIB_VERSION_*`, and sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`.
2. After the PR is merged, tag the commit and push it:
```
RELEASE_COMMIT=<merge commit of step 1>
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
```
3. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that sets `_PKG_VERSION_IS_RELEASE` to `false` and `_PKG_VERSION_PATCH` to `$PATCH + 1` and increases `_LIB_VERSION_REVISION`. If other maintainers are not present to approve the PR, it can be merged without ACKs.
4. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
## Maintenance release
Note that bugfixes only need to be backported to releases for which no compatible release without the bug exists.
1. If `$PATCH = 1`, create maintenance branch `$MAJOR.$MINOR`:
```
git checkout -b $MAJOR.$MINOR v$MAJOR.$MINOR.0
git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR
```
2. Open a pull request to the `$MAJOR.$MINOR` branch that
* includes the bugfixes,
* finalizes the release notes,
* bumps `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` (with commit message `"release: update PKG_ and LIB_VERSION for $MAJOR.$MINOR.$PATCH"`, for example).
3. After the PRs are merged, update the release branch and tag the commit:
```
git checkout $MAJOR.$MINOR && git pull
git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH"
```
4. Push tag:
```
git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH
```
5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md).
6. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md).

View file

@ -0,0 +1,819 @@
# The safegcd implementation in libsecp256k1 explained
This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files.
It is based on the paper
["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd)
by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version.
The actual implementation is in C of course, but for demonstration purposes Python3 is used here.
Most implementation aspects and optimizations are explained, except those that depend on the specific
number representation used in the C code.
## 1. Computing the Greatest Common Divisor (GCD) using divsteps
The algorithm from the paper (section 11), at a very high level, is this:
```python
def gcd(f, g):
"""Compute the GCD of an odd integer f and another integer g."""
assert f & 1 # require f to be odd
delta = 1 # additional state variable
while g != 0:
assert f & 1 # f will be odd in every iteration
if delta > 0 and g & 1:
delta, f, g = 1 - delta, g, (g - f) // 2
elif g & 1:
delta, f, g = 1 + delta, f, (g + f) // 2
else:
delta, f, g = 1 + delta, f, (g ) // 2
return abs(f)
```
It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop
keeps rewriting the variables *f* and *g* alongside a state variable *&delta;* that starts at *1*, until
*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a
"division step" (referred to as divstep in what follows).
For example, *gcd(21, 14)* would be computed as:
- Start with *&delta;=1 f=21 g=14*
- Take the third branch: *&delta;=2 f=21 g=7*
- Take the first branch: *&delta;=-1 f=7 g=-7*
- Take the second branch: *&delta;=0 f=7 g=0*
- The answer *|f| = 7*.
Why it works:
- Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper):
- (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*.
- (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even).
- Neither of those two operations change the GCD:
- For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a&thinsp;c* and *g=b&thinsp;c* for some integers *a*
and *b*. As *(g,g-f)=(b&thinsp;c,(b-a)c)* and *(f,f+g)=(a&thinsp;c,(a+b)c)*, the result clearly still has
common factor *c*. Reasoning in the other direction shows that no common factor can be added by
doing so either.
- For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove
it from *g*.
- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3).
- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the
gcd of *f'* and *0* is *|f'|* by definition, that is our answer.
Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at
the low-order bits of the variables to decide the next steps, and being easy to make
constant-time (in more low-level languages than Python). The *&delta;* parameter is necessary to
guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look
at high order bits.
Properties that will become important later:
- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*.
- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we
do not need to worry about rounding.
- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N*
bits of *f* and *g*, and on *&delta;*.
## 2. From GCDs to modular inverses
We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a&thinsp;x=1
mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is
prime and *0 < x < M*. In what follows, assume that the modular inverse exists.
It turns out this inverse can be computed as a side effect of computing the GCD by keeping track
of how the internal variables can be written as linear combinations of the inputs at every step
(see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)).
Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a&thinsp;x + b&thinsp;M = 1*.
Taking that expression *mod M* gives *a&thinsp;x mod M = 1*, and we see that *a* is the modular inverse of *x
mod M*.
A similar approach can be used to calculate modular inverses using the divsteps-based GCD
algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping
track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*.
*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M*
and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M
= 1*).
```python
def div2(M, x):
"""Helper routine to compute x/2 mod M (where M is odd)."""
assert M & 1
if x & 1: # If x is odd, make it even by adding M.
x += M
# x must be even now, so a clean division by 2 is possible.
return x // 2
def modinv(M, x):
"""Compute the inverse of x mod M (given that it exists, and M is odd)."""
assert M & 1
delta, f, g, d, e = 1, M, x, 0, 1
while g != 0:
# Note that while division by two for f and g is only ever done on even inputs, this is
# not true for d and e, so we need the div2 helper function.
if delta > 0 and g & 1:
delta, f, g, d, e = 1 - delta, g, (g - f) // 2, e, div2(M, e - d)
elif g & 1:
delta, f, g, d, e = 1 + delta, f, (g + f) // 2, d, div2(M, e + d)
else:
delta, f, g, d, e = 1 + delta, f, (g ) // 2, d, div2(M, e )
# Verify that the invariants d=f/x mod M, e=g/x mod M are maintained.
assert f % M == (d * x) % M
assert g % M == (e * x) % M
assert f == 1 or f == -1 # |f| is the GCD, it must be 1
# Because of invariant d = f/x (mod M), 1/x = d/f (mod M). As |f|=1, d/f = d*f.
return (d * f) % M
```
Also note that this approach to track *d* and *e* throughout the computation to determine the inverse
is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the
entire computation is determined (see section 3 below) and the inverse is computed from that.
The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to
be faster at the level of optimization we're able to do in C.
## 3. Batching multiple divsteps
Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)*
to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper):
```
t = [ u, v ]
[ q, r ]
[ out_f ] = (1/2 * t) * [ in_f ]
[ out_g ] = [ in_g ]
[ out_d ] = (1/2 * t) * [ in_d ] (mod M)
[ out_e ] [ in_e ]
```
where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is
taken. As above, the resulting *f* and *g* are always integers.
Performing multiple divsteps corresponds to a multiplication with the product of all the
individual divsteps' transition matrices. As each transition matrix consists of integers
divided by *2*, the product of these matrices will consist of integers divided by *2<sup>N</sup>* (see also
theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay
them: we compute the integer coefficients of the combined transition matrix scaled by *2<sup>N</sup>*, and
do one division by *2<sup>N</sup>* as a final step:
```python
def divsteps_n_matrix(delta, f, g):
"""Compute delta and transition matrix t after N divsteps (multiplied by 2^N)."""
u, v, q, r = 1, 0, 0, 1 # start with identity matrix
for _ in range(N):
if delta > 0 and g & 1:
delta, f, g, u, v, q, r = 1 - delta, g, (g - f) // 2, 2*q, 2*r, q-u, r-v
elif g & 1:
delta, f, g, u, v, q, r = 1 + delta, f, (g + f) // 2, 2*u, 2*v, q+u, r+v
else:
delta, f, g, u, v, q, r = 1 + delta, f, (g ) // 2, 2*u, 2*v, q , r
return delta, (u, v, q, r)
```
As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this
function to compute the transition matrix only needs to see those bottom bits. Furthermore all
intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*,
*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit
integers could set *N=62* and compute the full transition matrix for 62 steps at once without any
big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs
to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps.
We still need functions to compute:
```
[ out_f ] = (1/2^N * [ u, v ]) * [ in_f ]
[ out_g ] ( [ q, r ]) [ in_g ]
[ out_d ] = (1/2^N * [ u, v ]) * [ in_d ] (mod M)
[ out_e ] ( [ q, r ]) [ in_e ]
```
Because the divsteps transformation only ever divides even numbers by two, the result of *t&thinsp;[f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f*
and *g* will be multiple of *2<sup>N</sup>*, and division by *2<sup>N</sup>* is simply shifting them down:
```python
def update_fg(f, g, t):
"""Multiply matrix t/2^N with [f, g]."""
u, v, q, r = t
cf, cg = u*f + v*g, q*f + r*g
# (t / 2^N) should cleanly apply to [f,g] so the result of t*[f,g] should have N zero
# bottom bits.
assert cf % 2**N == 0
assert cg % 2**N == 0
return cf >> N, cg >> N
```
The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2<sup>N</sup> mod M*.
This is easy if we have precomputed *1/M mod 2<sup>N</sup>* (which always exists for odd *M*):
```python
def div2n(M, Mi, x):
"""Compute x/2^N mod M, given Mi = 1/M mod 2^N."""
assert (M * Mi) % 2**N == 1
# Find a factor m such that m*M has the same bottom N bits as x. We want:
# (m * M) mod 2^N = x mod 2^N
# <=> m mod 2^N = (x / M) mod 2^N
# <=> m mod 2^N = (x * Mi) mod 2^N
m = (Mi * x) % 2**N
# Subtract that multiple from x, cancelling its bottom N bits.
x -= m * M
# Now a clean division by 2^N is possible.
assert x % 2**N == 0
return (x >> N) % M
def update_de(d, e, t, M, Mi):
"""Multiply matrix t/2^N with [d, e], modulo M."""
u, v, q, r = t
cd, ce = u*d + v*e, q*d + r*e
return div2n(M, Mi, cd), div2n(M, Mi, ce)
```
With all of those, we can write a version of `modinv` that performs *N* divsteps at once:
```python3
def modinv(M, Mi, x):
"""Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
assert M & 1
delta, f, g, d, e = 1, M, x, 0, 1
while g != 0:
# Compute the delta and transition matrix t for the next N divsteps (this only needs
# (N+1)-bit signed integer arithmetic).
delta, t = divsteps_n_matrix(delta, f % 2**N, g % 2**N)
# Apply the transition matrix t to [f, g]:
f, g = update_fg(f, g, t)
# Apply the transition matrix t to [d, e]:
d, e = update_de(d, e, t, M, Mi)
return (d * f) % M
```
This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem
because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *&delta;* keeps
increasing). For variable time code such excess iterations will be mostly optimized away in later
sections.
## 4. Avoiding modulus operations
So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of
`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the
sign of *f*. These are relatively expensive operations when done generically.
To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range
*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus
operation at the end:
```python
def update_de(d, e, t, M, Mi):
"""Multiply matrix t/2^N with [d, e] mod M, given Mi=1/M mod 2^N."""
u, v, q, r = t
cd, ce = u*d + v*e, q*d + r*e
# Cancel out bottom N bits of cd and ce.
md = -((Mi * cd) % 2**N)
me = -((Mi * ce) % 2**N)
cd += md * M
ce += me * M
# And cleanly divide by 2**N.
return cd >> N, ce >> N
```
Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|*
never exceed *2<sup>N</sup>* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have
outputs whose absolute values are at most *2<sup>N</sup>* times the maximum absolute input value. In case the
inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming
*M > 1*, the multiplication results in numbers in range *(-2<sup>N</sup>M,2<sup>N</sup>M)*. Subtracting less than *2<sup>N</sup>*
times *M* to cancel out *N* bits brings that up to *(-2<sup>N+1</sup>M,2<sup>N</sup>M)*, and
dividing by *2<sup>N</sup>* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that
to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be
counteracted by incrementing *d* and *e* by *M* whenever they're negative:
```python
...
if d < 0:
d += M
if e < 0:
e += M
cd, ce = u*d + v*e, q*d + r*e
# Cancel out bottom N bits of cd and ce.
...
```
With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the
output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de`
invocations there are. In what follows, we will try to make this more efficient.
Note that increasing *d* by *M* is equal to incrementing *cd* by *u&thinsp;M* and *ce* by *q&thinsp;M*. Similarly,
increasing *e* by *M* is equal to incrementing *cd* by *v&thinsp;M* and *ce* by *r&thinsp;M*. So we could instead write:
```python
...
cd, ce = u*d + v*e, q*d + r*e
# Perform the equivalent of incrementing d, e by M when they're negative.
if d < 0:
cd += u*M
ce += q*M
if e < 0:
cd += v*M
ce += r*M
# Cancel out bottom N bits of cd and ce.
md = -((Mi * cd) % 2**N)
me = -((Mi * ce) % 2**N)
cd += md * M
ce += me * M
...
```
Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this
increment, and the decrement that cancels out bottom bits. The second one depends on the first
one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce*
at first, and using that to compute the final *md*, *me* values:
```python
def update_de(d, e, t, M, Mi):
"""Multiply matrix t/2^N with [d, e], modulo M."""
u, v, q, r = t
md, me = 0, 0
# Compute what multiples of M to add to cd and ce.
if d < 0:
md += u
me += q
if e < 0:
md += v
me += r
# Compute bottom N bits of t*[d,e] + M*[md,me].
cd, ce = (u*d + v*e + md*M) % 2**N, (q*d + r*e + me*M) % 2**N
# Correct md and me such that the bottom N bits of t*[d,e] + M*[md,me] are zero.
md -= (Mi * cd) % 2**N
me -= (Mi * ce) % 2**N
# Do the full computation.
cd, ce = u*d + v*e + md*M, q*d + r*e + me*M
# And cleanly divide by 2**N.
return cd >> N, ce >> N
```
One last optimization: we can avoid the *md&thinsp;M* and *me&thinsp;M* multiplications in the bottom bits of *cd*
and *ce* by moving them to the *md* and *me* correction:
```python
...
# Compute bottom N bits of t*[d,e].
cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
# Correct md and me such that the bottom N bits of t*[d,e]+M*[md,me] are zero.
# Note that this is not the same as {md = (-Mi * cd) % 2**N} etc. That would also result in N
# zero bottom bits, but isn't guaranteed to be a reduction of [0,2^N) compared to the
# previous md and me values, and thus would violate our bounds analysis.
md -= (Mi*cd + md) % 2**N
me -= (Mi*ce + me) % 2**N
...
```
The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same
range. That also means that the *d* value at the end of `modinv` will be in that range, while we want
a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the
conditional negation of *d* (based on the sign of *f*) into it as well:
```python
def normalize(sign, v, M):
"""Compute sign*v mod M, where v is in range (-2*M,M); output in [0,M)."""
assert sign == 1 or sign == -1
# v in (-2*M,M)
if v < 0:
v += M
# v in (-M,M). Now multiply v with sign (which can only be 1 or -1).
if sign == -1:
v = -v
# v in (-M,M)
if v < 0:
v += M
# v in [0,M)
return v
```
And calling it in `modinv` is simply:
```python
...
return normalize(f, d, M)
```
## 5. Constant-time operation
The primary selling point of the algorithm is fast constant-time operation. What code flow still
depends on the input data so far?
- the number of iterations of the while *g &ne; 0* loop in `modinv`
- the branches inside `divsteps_n_matrix`
- the sign checks in `update_de`
- the sign checks in `normalize`
To make the while loop in `modinv` constant time it can be replaced with a constant number of
iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit
inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is
sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of
*&lceil;724/N&rceil;* times.
To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise
operations (and hope the C compiler isn't smart enough to turn them back into branches; see
`ctime_tests.c` for automated tests that this isn't the case). To do so, observe that a
divstep can be written instead as (compare to the inner loop of `gcd` in section 1).
```python
x = -f if delta > 0 else f # set x equal to (input) -f or f
if g & 1:
g += x # set g to (input) g-f or g+f
if delta > 0:
delta = -delta
f += g # set f to (input) g (note that g was set to g-f before)
delta += 1
g >>= 1
```
To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the
definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As
*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows
that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then
*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*.
Using this we can write:
```python
x = -f if delta > 0 else f
```
in constant-time form as:
```python
c1 = (-delta) >> 63
# Conditionally negate f based on c1:
x = (f ^ c1) - c1
```
To use that trick, we need a helper mask variable *c1* that resolves the condition *&delta;>0* to *-1*
(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by
the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see
`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all
numbers in range *[-2<sup>63</sup>,0)* to *-1*, and numbers in range *[0,2<sup>63</sup>)* to *0*.
Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write:
```python
if g & 1:
g += x
```
as:
```python
# Compute c2=0 if g is even and c2=-1 if g is odd.
c2 = -(g & 1)
# This masks out x if g is even, and leaves x be if g is odd.
g += x & c2
```
Using the conditional negation trick again we can write:
```python
if g & 1:
if delta > 0:
delta = -delta
```
as:
```python
# Compute c3=-1 if g is odd and delta>0, and 0 otherwise.
c3 = c1 & c2
# Conditionally negate delta based on c3:
delta = (delta ^ c3) - c3
```
Finally:
```python
if g & 1:
if delta > 0:
f += g
```
becomes:
```python
f += g & c3
```
It turns out that this can be implemented more efficiently by applying the substitution
*&eta;=-&delta;*. In this representation, negating *&delta;* corresponds to negating *&eta;*, and incrementing
*&delta;* corresponds to decrementing *&eta;*. This allows us to remove the negation in the *c1*
computation:
```python
# Compute a mask c1 for eta < 0, and compute the conditional negation x of f:
c1 = eta >> 63
x = (f ^ c1) - c1
# Compute a mask c2 for odd g, and conditionally add x to g:
c2 = -(g & 1)
g += x & c2
# Compute a mask c for (eta < 0) and odd (input) g, and use it to conditionally negate eta,
# and add g to f:
c3 = c1 & c2
eta = (eta ^ c3) - c3
f += g & c3
# Incrementing delta corresponds to decrementing eta.
eta -= 1
g >>= 1
```
A variant of divsteps with better worst-case performance can be used instead: starting *&delta;* at
*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs
(which can be shown using convex hull analysis). In this case, the substitution *&zeta;=-(&delta;+1/2)*
is used instead to keep the variable integral. Incrementing *&delta;* by *1* still translates to
decrementing *&zeta;* by *1*, but negating *&delta;* now corresponds to going from *&zeta;* to *-(&zeta;+1)*, or
*~&zeta;*. Doing that conditionally based on *c3* is simply:
```python
...
c3 = c1 & c2
zeta ^= c3
...
```
By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to
also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of
`divsteps_n_matrix` is obtained. The full code will be in section 7.
These bit fiddling tricks can also be used to make the conditional negations and additions in
`update_de` and `normalize` constant-time.
## 6. Variable-time optimizations
In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time.
Constant time operations are only necessary when computing modular inverses of secret data. In
other cases, it slows down calculations unnecessarily. In this section, we will construct a
faster non-constant time `divsteps_n_matrix` function.
To do so, first consider yet another way of writing the inner loop of divstep operations in
`gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use
the original version with initial *&delta;=1* and *&eta;=-&delta;* here.
```python
for _ in range(N):
if g & 1 and eta < 0:
eta, f, g = -eta, g, -f
if g & 1:
g += f
eta -= 1
g >>= 1
```
Whenever *g* is even, the loop only shifts *g* down and decreases *&eta;*. When *g* ends in multiple zero
bits, these iterations can be consolidated into one step. This requires counting the bottom zero
bits efficiently, which is possible on most platforms; it is abstracted here as the function
`count_trailing_zeros`.
```python
def count_trailing_zeros(v):
"""
When v is zero, consider all N zero bits as "trailing".
For a non-zero value v, find z such that v=(d<<z) for some odd d.
"""
if v == 0:
return N
else:
return (v & -v).bit_length() - 1
i = N # divsteps left to do
while True:
# Get rid of all bottom zeros at once. In the first iteration, g may be odd and the following
# lines have no effect (until "if eta < 0").
zeros = min(i, count_trailing_zeros(g))
eta -= zeros
g >>= zeros
i -= zeros
if i == 0:
break
# We know g is odd now
if eta < 0:
eta, f, g = -eta, g, -f
g += f
# g is even now, and the eta decrement and g shift will happen in the next loop.
```
We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever
there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as
well.
Observe that as long as *&eta; &geq; 0*, the loop does not modify *f*. Instead, it cancels out bottom
bits of *g* and shifts them out, and decreases *&eta;* and *i* accordingly - interrupting only when *&eta;*
becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to
*g* to cancel out multiple bottom bits, and then shifting them out.
It is easy to find what that multiple is: we want a number *w* such that *g+w&thinsp;f* has a few bottom
zero bits. If that number of bits is *L*, we want *g+w&thinsp;f mod 2<sup>L</sup> = 0*, or *w = -g/f mod 2<sup>L</sup>*. Since *f*
is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before
doing more) or more than *&eta;+1* steps (as we'd run `eta, f, g = -eta, g, -f` at that point), but
apart from that, we're only limited by the complexity of computing *w*.
This code demonstrates how to cancel up to 4 bits per step:
```python
NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
i = N
while True:
zeros = min(i, count_trailing_zeros(g))
eta -= zeros
g >>= zeros
i -= zeros
if i == 0:
break
# We know g is odd now
if eta < 0:
eta, f, g = -eta, g, -f
# Compute limit on number of bits to cancel
limit = min(min(eta + 1, i), 4)
# Compute w = -g/f mod 2**limit, using the table value for -1/f mod 2**4. Note that f is
# always odd, so its inverse modulo a power of two always exists.
w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
# As w = -g/f mod (2**limit), g+w*f mod 2**limit = 0 mod 2**limit.
g += w * f
assert g % (2**limit) == 0
# The next iteration will now shift out at least limit bottom zero bits from g.
```
By using a bigger table more bits can be cancelled at once. The table can also be implemented
as a formula. Several formulas are known for computing modular inverses modulo powers of two;
some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pages 245-247.
Here we need the negated modular inverse, which is a simple transformation of those:
- Instead of a 3-bit table:
- *-f* or *f ^ 6*
- Instead of a 4-bit table:
- *1 - f(f + 1)*
- *-(f + (((f + 1) & 4) << 1))*
- For larger tables the following technique can be used: if *w=-1/f mod 2<sup>L</sup>*, then *w(w&thinsp;f+2)* is
*-1/f mod 2<sup>2L</sup>*. This allows extending the previous formulas (or tables). In particular we
have this 6-bit function (based on the 3-bit function above):
- *f(f<sup>2</sup> - 2)*
This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in
`divsteps_n_matrix`, gives a significantly faster, but non-constant time version.
## 7. Final Python version
All together we need the following functions:
- A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function
from section 2, but with its loop replaced by a variant of the constant-time divstep from
section 5, extended to handle *u*, *v*, *q*, *r*:
```python
def divsteps_n_matrix(zeta, f, g):
"""Compute zeta and transition matrix t after N divsteps (multiplied by 2^N)."""
u, v, q, r = 1, 0, 0, 1 # start with identity matrix
for _ in range(N):
c1 = zeta >> 63
# Compute x, y, z as conditionally-negated versions of f, u, v.
x, y, z = (f ^ c1) - c1, (u ^ c1) - c1, (v ^ c1) - c1
c2 = -(g & 1)
# Conditionally add x, y, z to g, q, r.
g, q, r = g + (x & c2), q + (y & c2), r + (z & c2)
c1 &= c2 # reusing c1 here for the earlier c3 variable
zeta = (zeta ^ c1) - 1 # inlining the unconditional zeta decrement here
# Conditionally add g, q, r to f, u, v.
f, u, v = f + (g & c1), u + (q & c1), v + (r & c1)
# When shifting g down, don't shift q, r, as we construct a transition matrix multiplied
# by 2^N. Instead, shift f's coefficients u and v up.
g, u, v = g >> 1, u << 1, v << 1
return zeta, (u, v, q, r)
```
- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time
changes to `update_de` from section 5:
```python
def update_fg(f, g, t):
"""Multiply matrix t/2^N with [f, g]."""
u, v, q, r = t
cf, cg = u*f + v*g, q*f + r*g
return cf >> N, cg >> N
def update_de(d, e, t, M, Mi):
"""Multiply matrix t/2^N with [d, e], modulo M."""
u, v, q, r = t
d_sign, e_sign = d >> 257, e >> 257
md, me = (u & d_sign) + (v & e_sign), (q & d_sign) + (r & e_sign)
cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N
md -= (Mi*cd + md) % 2**N
me -= (Mi*ce + me) % 2**N
cd, ce = u*d + v*e + M*md, q*d + r*e + M*me
return cd >> N, ce >> N
```
- The `normalize` function from section 4, made constant time as well:
```python
def normalize(sign, v, M):
"""Compute sign*v mod M, where v in (-2*M,M); output in [0,M)."""
v_sign = v >> 257
# Conditionally add M to v.
v += M & v_sign
c = (sign - 1) >> 1
# Conditionally negate v.
v = (v ^ c) - c
v_sign = v >> 257
# Conditionally add M to v again.
v += M & v_sign
return v
```
- And finally the `modinv` function too, adapted to use *&zeta;* instead of *&delta;*, and using the fixed
iteration count from section 5:
```python
def modinv(M, Mi, x):
"""Compute the modular inverse of x mod M, given Mi=1/M mod 2^N."""
zeta, f, g, d, e = -1, M, x, 0, 1
for _ in range((590 + N - 1) // N):
zeta, t = divsteps_n_matrix(zeta, f % 2**N, g % 2**N)
f, g = update_fg(f, g, t)
d, e = update_de(d, e, t, M, Mi)
return normalize(f, d, M)
```
- To get a variable time version, replace the `divsteps_n_matrix` function with one that uses the
divsteps loop from section 5, and a `modinv` version that calls it without the fixed iteration
count:
```python
NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n
def divsteps_n_matrix_var(eta, f, g):
"""Compute eta and transition matrix t after N divsteps (multiplied by 2^N)."""
u, v, q, r = 1, 0, 0, 1
i = N
while True:
zeros = min(i, count_trailing_zeros(g))
eta, i = eta - zeros, i - zeros
g, u, v = g >> zeros, u << zeros, v << zeros
if i == 0:
break
if eta < 0:
eta, f, u, v, g, q, r = -eta, g, q, r, -f, -u, -v
limit = min(min(eta + 1, i), 4)
w = (g * NEGINV16[(f & 15) // 2]) % (2**limit)
g, q, r = g + w*f, q + w*u, r + w*v
return eta, (u, v, q, r)
def modinv_var(M, Mi, x):
"""Compute the modular inverse of x mod M, given Mi = 1/M mod 2^N."""
eta, f, g, d, e = -1, M, x, 0, 1
while g != 0:
eta, t = divsteps_n_matrix_var(eta, f % 2**N, g % 2**N)
f, g = update_fg(f, g, t)
d, e = update_de(d, e, t, M, Mi)
return normalize(f, d, Mi)
```
## 8. From GCDs to Jacobi symbol
We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an
extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we
make corresponding updates to *j* using
[properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties):
* *((g/2) | f)* is either *(g | f)* or *-(g | f)*, depending on the value of *f mod 8* (negating if it's *3* or *5*).
* *(f | g)* is either *(g | f)* or *-(g | f)*, depending on *f mod 4* and *g mod 4* (negating if both are *3*).
These updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied
very quickly, as long as we keep track of a few additional bits of *f* and *g*. Overall, this
calculation is slightly simpler than the one for the modular inverse because we no longer need to
keep track of *d* and *e*.
However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for
positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative
values. We resolve this by using the following modified steps:
```python
# Before
if delta > 0 and g & 1:
delta, f, g = 1 - delta, g, (g - f) // 2
# After
if delta > 0 and g & 1:
delta, f, g = 1 - delta, g, (g + f) // 2
```
The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4
and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm
will converge. The justification for posdivsteps is completely empirical: in practice, it appears
that the vast majority of nonzero inputs converge to *f=g=gcd(f<sub>0</sub>, g<sub>0</sub>)* in a
number of steps proportional to their logarithm.
Note that:
- We require inputs to satisfy *gcd(x, M) = 1*, as otherwise *f=1* is not reached.
- We require inputs *x &neq; 0*, because applying posdivstep with *g=0* has no effect.
- We need to update the termination condition from *g=0* to *f=1*.
We account for the possibility of nonconvergence by only performing a bounded number of
posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not
yet been found.
The optimizations in sections 3-7 above are described in the context of the original divsteps, but
in the C implementation we also adapt most of them (not including "avoiding modulus operations",
since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate
Jacobi symbols for secret data) to the posdivsteps version.

View file

@ -0,0 +1,34 @@
add_library(example INTERFACE)
target_include_directories(example INTERFACE
${PROJECT_SOURCE_DIR}/include
)
target_compile_options(example INTERFACE
$<$<C_COMPILER_ID:MSVC>:/wd4005>
)
target_link_libraries(example INTERFACE
$<$<PLATFORM_ID:Windows>:bcrypt>
)
if(SECP256K1_BUILD_SHARED)
target_link_libraries(example INTERFACE secp256k1)
elseif(SECP256K1_BUILD_STATIC)
target_link_libraries(example INTERFACE secp256k1_static)
if(MSVC)
target_link_options(example INTERFACE /IGNORE:4217)
endif()
endif()
add_executable(ecdsa_example ecdsa.c)
target_link_libraries(ecdsa_example example)
add_test(ecdsa_example ecdsa_example)
if(SECP256K1_ENABLE_MODULE_ECDH)
add_executable(ecdh_example ecdh.c)
target_link_libraries(ecdh_example example)
add_test(ecdh_example ecdh_example)
endif()
if(SECP256K1_ENABLE_MODULE_SCHNORRSIG)
add_executable(schnorr_example schnorr.c)
target_link_libraries(schnorr_example example)
add_test(schnorr_example schnorr_example)
endif()

View file

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

122
vendor/secp256k1/repo/examples/ecdh.c vendored Normal file
View file

@ -0,0 +1,122 @@
/*************************************************************************
* Written in 2020-2022 by Elichai Turkel *
* To the extent possible under law, the author(s) have dedicated all *
* copyright and related and neighboring rights to the software in this *
* file to the public domain worldwide. This software is distributed *
* without any warranty. For the CC0 Public Domain Dedication, see *
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
*************************************************************************/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <secp256k1.h>
#include <secp256k1_ecdh.h>
#include "examples_util.h"
int main(void) {
unsigned char seckey1[32];
unsigned char seckey2[32];
unsigned char compressed_pubkey1[33];
unsigned char compressed_pubkey2[33];
unsigned char shared_secret1[32];
unsigned char shared_secret2[32];
unsigned char randomize[32];
int return_val;
size_t len;
secp256k1_pubkey pubkey1;
secp256k1_pubkey pubkey2;
/* Before we can call actual API functions, we need to create a "context". */
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if (!fill_random(randomize, sizeof(randomize))) {
printf("Failed to generate randomness\n");
return 1;
}
/* Randomizing the context is recommended to protect against side-channel
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
* information about it. This should never fail. */
return_val = secp256k1_context_randomize(ctx, randomize);
assert(return_val);
/*** Key Generation ***/
/* If the secret key is zero or out of range (bigger than secp256k1's
* order), we try to sample a new key. Note that the probability of this
* happening is negligible. */
while (1) {
if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) {
break;
}
}
/* Public key creation using a valid context with a verified secret key should never fail */
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1);
assert(return_val);
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2);
assert(return_val);
/* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
len = sizeof(compressed_pubkey1);
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED);
assert(return_val);
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
assert(len == sizeof(compressed_pubkey1));
/* Serialize pubkey2 in a compressed form (33 bytes) */
len = sizeof(compressed_pubkey2);
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
assert(return_val);
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
assert(len == sizeof(compressed_pubkey2));
/*** Creating the shared secret ***/
/* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified
* seckey and valid pubkey */
return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL);
assert(return_val);
/* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified
* seckey and valid pubkey */
return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL);
assert(return_val);
/* Both parties should end up with the same shared secret */
return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
assert(return_val == 0);
printf("Secret Key1: ");
print_hex(seckey1, sizeof(seckey1));
printf("Compressed Pubkey1: ");
print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
printf("\nSecret Key2: ");
print_hex(seckey2, sizeof(seckey2));
printf("Compressed Pubkey2: ");
print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
printf("\nShared Secret: ");
print_hex(shared_secret1, sizeof(shared_secret1));
/* This will clear everything from the context and free the memory */
secp256k1_context_destroy(ctx);
/* It's best practice to try to clear secrets from memory after using them.
* This is done because some bugs can allow an attacker to leak memory, for
* example through "out of bounds" array access (see Heartbleed), Or the OS
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
*
* Here we are preventing these writes from being optimized out, as any good compiler
* will remove any writes that aren't used. */
secure_erase(seckey1, sizeof(seckey1));
secure_erase(seckey2, sizeof(seckey2));
secure_erase(shared_secret1, sizeof(shared_secret1));
secure_erase(shared_secret2, sizeof(shared_secret2));
return 0;
}

139
vendor/secp256k1/repo/examples/ecdsa.c vendored Normal file
View file

@ -0,0 +1,139 @@
/*************************************************************************
* Written in 2020-2022 by Elichai Turkel *
* To the extent possible under law, the author(s) have dedicated all *
* copyright and related and neighboring rights to the software in this *
* file to the public domain worldwide. This software is distributed *
* without any warranty. For the CC0 Public Domain Dedication, see *
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
*************************************************************************/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <secp256k1.h>
#include "examples_util.h"
int main(void) {
/* Instead of signing the message directly, we must sign a 32-byte hash.
* Here the message is "Hello, world!" and the hash function was SHA-256.
* An actual implementation should just call SHA-256, but this example
* hardcodes the output to avoid depending on an additional library.
* See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */
unsigned char msg_hash[32] = {
0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
};
unsigned char seckey[32];
unsigned char randomize[32];
unsigned char compressed_pubkey[33];
unsigned char serialized_signature[64];
size_t len;
int is_signature_valid, is_signature_valid2;
int return_val;
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
/* Before we can call actual API functions, we need to create a "context". */
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if (!fill_random(randomize, sizeof(randomize))) {
printf("Failed to generate randomness\n");
return 1;
}
/* Randomizing the context is recommended to protect against side-channel
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
* information about it. This should never fail. */
return_val = secp256k1_context_randomize(ctx, randomize);
assert(return_val);
/*** Key Generation ***/
/* If the secret key is zero or out of range (bigger than secp256k1's
* order), we try to sample a new key. Note that the probability of this
* happening is negligible. */
while (1) {
if (!fill_random(seckey, sizeof(seckey))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_ec_seckey_verify(ctx, seckey)) {
break;
}
}
/* Public key creation using a valid context with a verified secret key should never fail */
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey);
assert(return_val);
/* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */
len = sizeof(compressed_pubkey);
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
assert(return_val);
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
assert(len == sizeof(compressed_pubkey));
/*** Signing ***/
/* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a
* custom nonce function, passing `NULL` will use the RFC-6979 safe default.
* Signing with a valid context, verified secret key
* and the default nonce function should never fail. */
return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL);
assert(return_val);
/* Serialize the signature in a compact form. Should always return 1
* according to the documentation in secp256k1.h. */
return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig);
assert(return_val);
/*** Verification ***/
/* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) {
printf("Failed parsing the signature\n");
return 1;
}
/* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) {
printf("Failed parsing the public key\n");
return 1;
}
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
printf("Secret Key: ");
print_hex(seckey, sizeof(seckey));
printf("Public Key: ");
print_hex(compressed_pubkey, sizeof(compressed_pubkey));
printf("Signature: ");
print_hex(serialized_signature, sizeof(serialized_signature));
/* This will clear everything from the context and free the memory */
secp256k1_context_destroy(ctx);
/* Bonus example: if all we need is signature verification (and no key
generation or signing), we don't need to use a context created via
secp256k1_context_create(). We can simply use the static (i.e., global)
context secp256k1_context_static. See its description in
include/secp256k1.h for details. */
is_signature_valid2 = secp256k1_ecdsa_verify(secp256k1_context_static,
&sig, msg_hash, &pubkey);
assert(is_signature_valid2 == is_signature_valid);
/* It's best practice to try to clear secrets from memory after using them.
* This is done because some bugs can allow an attacker to leak memory, for
* example through "out of bounds" array access (see Heartbleed), Or the OS
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
*
* Here we are preventing these writes from being optimized out, as any good compiler
* will remove any writes that aren't used. */
secure_erase(seckey, sizeof(seckey));
return 0;
}

View file

@ -0,0 +1,102 @@
/*************************************************************************
* Copyright (c) 2020-2021 Elichai Turkel *
* Distributed under the CC0 software license, see the accompanying file *
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
*************************************************************************/
/*
* This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
* It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
*
* Platform randomness sources:
* Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
* macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
* FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
* OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
* Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
*/
#if defined(_WIN32)
#include <windows.h>
#include <ntstatus.h>
#include <bcrypt.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
#include <sys/random.h>
#elif defined(__OpenBSD__)
#include <unistd.h>
#else
#error "Couldn't identify the OS"
#endif
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
/* Returns 1 on success, and 0 on failure. */
static int fill_random(unsigned char* data, size_t size) {
#if defined(_WIN32)
NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (res != STATUS_SUCCESS || size > ULONG_MAX) {
return 0;
} else {
return 1;
}
#elif defined(__linux__) || defined(__FreeBSD__)
/* If `getrandom(2)` is not available you should fallback to /dev/urandom */
ssize_t res = getrandom(data, size, 0);
if (res < 0 || (size_t)res != size ) {
return 0;
} else {
return 1;
}
#elif defined(__APPLE__) || defined(__OpenBSD__)
/* If `getentropy(2)` is not available you should fallback to either
* `SecRandomCopyBytes` or /dev/urandom */
int res = getentropy(data, size);
if (res == 0) {
return 1;
} else {
return 0;
}
#endif
return 0;
}
static void print_hex(unsigned char* data, size_t size) {
size_t i;
printf("0x");
for (i = 0; i < size; i++) {
printf("%02x", data[i]);
}
printf("\n");
}
#if defined(_MSC_VER)
// For SecureZeroMemory
#include <Windows.h>
#endif
/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */
static SECP256K1_INLINE void secure_erase(void *ptr, size_t len) {
#if defined(_MSC_VER)
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
SecureZeroMemory(ptr, len);
#elif defined(__GNUC__)
/* We use a memory barrier that scares the compiler away from optimizing out the memset.
*
* Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f
* in BoringSSL (ISC License):
* As best as we can tell, this is sufficient to break any optimisations that
* might try to eliminate "superfluous" memsets.
* This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is
* pretty efficient, because the compiler can still implement the memset() efficently,
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
* Yang et al. (USENIX Security 2017) for more background.
*/
memset(ptr, 0, len);
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#else
void *(*volatile const volatile_memset)(void *, int, size_t) = memset;
volatile_memset(ptr, 0, len);
#endif
}

156
vendor/secp256k1/repo/examples/schnorr.c vendored Normal file
View file

@ -0,0 +1,156 @@
/*************************************************************************
* Written in 2020-2022 by Elichai Turkel *
* To the extent possible under law, the author(s) have dedicated all *
* copyright and related and neighboring rights to the software in this *
* file to the public domain worldwide. This software is distributed *
* without any warranty. For the CC0 Public Domain Dedication, see *
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
*************************************************************************/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <secp256k1.h>
#include <secp256k1_extrakeys.h>
#include <secp256k1_schnorrsig.h>
#include "examples_util.h"
int main(void) {
unsigned char msg[12] = "Hello World!";
unsigned char msg_hash[32];
unsigned char tag[17] = "my_fancy_protocol";
unsigned char seckey[32];
unsigned char randomize[32];
unsigned char auxiliary_rand[32];
unsigned char serialized_pubkey[32];
unsigned char signature[64];
int is_signature_valid, is_signature_valid2;
int return_val;
secp256k1_xonly_pubkey pubkey;
secp256k1_keypair keypair;
/* Before we can call actual API functions, we need to create a "context". */
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if (!fill_random(randomize, sizeof(randomize))) {
printf("Failed to generate randomness\n");
return 1;
}
/* Randomizing the context is recommended to protect against side-channel
* leakage See `secp256k1_context_randomize` in secp256k1.h for more
* information about it. This should never fail. */
return_val = secp256k1_context_randomize(ctx, randomize);
assert(return_val);
/*** Key Generation ***/
/* If the secret key is zero or out of range (bigger than secp256k1's
* order), we try to sample a new key. Note that the probability of this
* happening is negligible. */
while (1) {
if (!fill_random(seckey, sizeof(seckey))) {
printf("Failed to generate randomness\n");
return 1;
}
/* Try to create a keypair with a valid context, it should only fail if
* the secret key is zero or out of range. */
if (secp256k1_keypair_create(ctx, &keypair, seckey)) {
break;
}
}
/* Extract the X-only public key from the keypair. We pass NULL for
* `pk_parity` as the parity isn't needed for signing or verification.
* `secp256k1_keypair_xonly_pub` supports returning the parity for
* other use cases such as tests or verifying Taproot tweaks.
* This should never fail with a valid context and public key. */
return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);
assert(return_val);
/* Serialize the public key. Should always return 1 for a valid public key. */
return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);
assert(return_val);
/*** Signing ***/
/* Instead of signing (possibly very long) messages directly, we sign a
* 32-byte hash of the message in this example.
*
* We use secp256k1_tagged_sha256 to create this hash. This function expects
* a context-specific "tag", which restricts the context in which the signed
* messages should be considered valid. For example, if protocol A mandates
* to use the tag "my_fancy_protocol" and protocol B mandates to use the tag
* "my_boring_protocol", then signed messages from protocol A will never be
* valid in protocol B (and vice versa), even if keys are reused across
* protocols. This implements "domain separation", which is considered good
* practice. It avoids attacks in which users are tricked into signing a
* message that has intended consequences in the intended context (e.g.,
* protocol A) but would have unintended consequences if it were valid in
* some other context (e.g., protocol B). */
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
assert(return_val);
/* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
printf("Failed to generate randomness\n");
return 1;
}
/* Generate a Schnorr signature.
*
* We use the secp256k1_schnorrsig_sign32 function that provides a simple
* interface for signing 32-byte messages (which in our case is a hash of
* the actual message). BIP-340 recommends passing 32 bytes of randomness
* to the signing function to improve security against side-channel attacks.
* Signing with a valid context, a 32-byte message, a verified keypair, and
* any 32 bytes of auxiliary random data should never fail. */
return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);
assert(return_val);
/*** Verification ***/
/* Deserialize the public key. This will return 0 if the public key can't
* be parsed correctly */
if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {
printf("Failed parsing the public key\n");
return 1;
}
/* Compute the tagged hash on the received messages using the same tag as the signer. */
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
assert(return_val);
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
printf("Secret Key: ");
print_hex(seckey, sizeof(seckey));
printf("Public Key: ");
print_hex(serialized_pubkey, sizeof(serialized_pubkey));
printf("Signature: ");
print_hex(signature, sizeof(signature));
/* This will clear everything from the context and free the memory */
secp256k1_context_destroy(ctx);
/* Bonus example: if all we need is signature verification (and no key
generation or signing), we don't need to use a context created via
secp256k1_context_create(). We can simply use the static (i.e., global)
context secp256k1_context_static. See its description in
include/secp256k1.h for details. */
is_signature_valid2 = secp256k1_schnorrsig_verify(secp256k1_context_static,
signature, msg_hash, 32, &pubkey);
assert(is_signature_valid2 == is_signature_valid);
/* It's best practice to try to clear secrets from memory after using them.
* This is done because some bugs can allow an attacker to leak memory, for
* example through "out of bounds" array access (see Heartbleed), Or the OS
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
*
* Here we are preventing these writes from being optimized out, as any good compiler
* will remove any writes that aren't used. */
secure_erase(seckey, sizeof(seckey));
return 0;
}

View file

@ -0,0 +1,914 @@
#ifndef SECP256K1_H
#define SECP256K1_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/** Unless explicitly stated all pointer arguments must not be NULL.
*
* The following rules specify the order of arguments in API calls:
*
* 1. Context pointers go first, followed by output arguments, combined
* output/input arguments, and finally input-only arguments.
* 2. Array lengths always immediately follow the argument whose length
* they describe, even if this violates rule 1.
* 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated
* later go first. This means: signatures, public nonces, secret nonces,
* messages, public keys, secret keys, tweaks.
* 4. Arguments that are not data pointers go last, from more complex to less
* complex: function pointers, algorithm names, messages, void pointers,
* counts, flags, booleans.
* 5. Opaque data pointers follow the function pointer they are to be passed to.
*/
/** Opaque data structure that holds context information
*
* The primary purpose of context objects is to store randomization data for
* enhanced protection against side-channel leakage. This protection is only
* effective if the context is randomized after its creation. See
* secp256k1_context_create for creation of contexts and
* secp256k1_context_randomize for randomization.
*
* A secondary purpose of context objects is to store pointers to callback
* functions that the library will call when certain error states arise. See
* secp256k1_context_set_error_callback as well as
* secp256k1_context_set_illegal_callback for details. Future library versions
* may use context objects for additional purposes.
*
* A constructed context can safely be used from multiple threads
* simultaneously, but API calls that take a non-const pointer to a context
* need exclusive access to it. In particular this is the case for
* secp256k1_context_destroy, secp256k1_context_preallocated_destroy,
* and secp256k1_context_randomize.
*
* Regarding randomization, either do it once at creation time (in which case
* you do not need any locking for the other calls), or use a read-write lock.
*/
typedef struct secp256k1_context_struct secp256k1_context;
/** Opaque data structure that holds rewritable "scratch space"
*
* The purpose of this structure is to replace dynamic memory allocations,
* because we target architectures where this may not be available. It is
* essentially a resizable (within specified parameters) block of bytes,
* which is initially created either by memory allocation or TODO as a pointer
* into some fixed rewritable space.
*
* Unlike the context object, this cannot safely be shared between threads
* without additional synchronization logic.
*/
typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;
/** Opaque data structure that holds a parsed and valid public key.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission,
* use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To
* compare keys, use secp256k1_ec_pubkey_cmp.
*/
typedef struct {
unsigned char data[64];
} secp256k1_pubkey;
/** Opaque data structured that holds a parsed ECDSA signature.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage, transmission, or
* comparison, use the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_parse_* functions.
*/
typedef struct {
unsigned char data[64];
} secp256k1_ecdsa_signature;
/** A pointer to a function to deterministically generate a nonce.
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
* In: msg32: the 32-byte message hash being verified (will not be NULL)
* key32: pointer to a 32-byte secret key (will not be NULL)
* algo16: pointer to a 16-byte array describing the signature
* algorithm (will be NULL for ECDSA for compatibility).
* data: Arbitrary data pointer that is passed through.
* attempt: how many iterations we have tried to find a nonce.
* This will almost always be 0, but different attempt values
* are required to result in a different nonce.
*
* Except for test cases, this function should compute some cryptographic hash of
* the message, the algorithm, the key and the attempt.
*/
typedef int (*secp256k1_nonce_function)(
unsigned char *nonce32,
const unsigned char *msg32,
const unsigned char *key32,
const unsigned char *algo16,
void *data,
unsigned int attempt
);
# if !defined(SECP256K1_GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define SECP256K1_GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define SECP256K1_GNUC_PREREQ(_maj,_min) 0
# endif
# endif
# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
# if SECP256K1_GNUC_PREREQ(2,7)
# define SECP256K1_INLINE __inline__
# elif (defined(_MSC_VER))
# define SECP256K1_INLINE __inline
# else
# define SECP256K1_INLINE
# endif
# else
# define SECP256K1_INLINE inline
# endif
/* When this header is used at build-time the SECP256K1_BUILD define needs to be set
* to correctly setup export attributes and nullness checks. This is normally done
* by secp256k1.c but to guard against this header being included before secp256k1.c
* has had a chance to set the define (e.g. via test harnesses that just includes
* secp256k1.c) we set SECP256K1_NO_BUILD when this header is processed without the
* BUILD define so this condition can be caught.
*/
#ifndef SECP256K1_BUILD
# define SECP256K1_NO_BUILD
#endif
/* Symbol visibility. See libtool manual, section "Windows DLLs". */
#if defined(_WIN32) && !defined(__GNUC__)
# ifdef SECP256K1_BUILD
# ifdef DLL_EXPORT
# define SECP256K1_API __declspec (dllexport)
# define SECP256K1_API_VAR extern __declspec (dllexport)
# endif
# elif defined _MSC_VER
# define SECP256K1_API
# define SECP256K1_API_VAR extern __declspec (dllimport)
# elif defined DLL_EXPORT
# define SECP256K1_API __declspec (dllimport)
# define SECP256K1_API_VAR extern __declspec (dllimport)
# endif
#endif
#ifndef SECP256K1_API
# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
# define SECP256K1_API __attribute__ ((visibility ("default")))
# define SECP256K1_API_VAR extern __attribute__ ((visibility ("default")))
# else
# define SECP256K1_API
# define SECP256K1_API_VAR extern
# endif
#endif
/* Warning attributes
* NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out
* some paranoid null checks. */
# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
# else
# define SECP256K1_WARN_UNUSED_RESULT
# endif
# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4)
# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
# else
# define SECP256K1_ARG_NONNULL(_x)
# endif
/* Attribute for marking functions, types, and variables as deprecated */
#if !defined(SECP256K1_BUILD) && defined(__has_attribute)
# if __has_attribute(__deprecated__)
# define SECP256K1_DEPRECATED(_msg) __attribute__ ((__deprecated__(_msg)))
# else
# define SECP256K1_DEPRECATED(_msg)
# endif
#else
# define SECP256K1_DEPRECATED(_msg)
#endif
/* All flags' lower 8 bits indicate what they're for. Do not use directly. */
#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1)
#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1)
/* The higher bits contain the actual data. Do not use directly. */
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10)
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
/** Context flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
* secp256k1_context_preallocated_create. */
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
/** Deprecated context flags. These flags are treated equivalent to SECP256K1_CONTEXT_NONE. */
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
/* Testing flag. Do not use. */
#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
/** Flag to pass to secp256k1_ec_pubkey_serialize. */
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
/** Prefix byte used to tag various encoded curvepoints for specific purposes */
#define SECP256K1_TAG_PUBKEY_EVEN 0x02
#define SECP256K1_TAG_PUBKEY_ODD 0x03
#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04
#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06
#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07
/** A built-in constant secp256k1 context object with static storage duration, to be
* used in conjunction with secp256k1_selftest.
*
* This context object offers *only limited functionality* , i.e., it cannot be used
* for API functions that perform computations involving secret keys, e.g., signing
* and public key generation. If this restriction applies to a specific API function,
* it is mentioned in its documentation. See secp256k1_context_create if you need a
* full context object that supports all functionality offered by the library.
*
* It is highly recommended to call secp256k1_selftest before using this context.
*/
SECP256K1_API_VAR const secp256k1_context *secp256k1_context_static;
/** Deprecated alias for secp256k1_context_static. */
SECP256K1_API_VAR const secp256k1_context *secp256k1_context_no_precomp
SECP256K1_DEPRECATED("Use secp256k1_context_static instead");
/** Perform basic self tests (to be used in conjunction with secp256k1_context_static)
*
* This function performs self tests that detect some serious usage errors and
* similar conditions, e.g., when the library is compiled for the wrong endianness.
* This is a last resort measure to be used in production. The performed tests are
* very rudimentary and are not intended as a replacement for running the test
* binaries.
*
* It is highly recommended to call this before using secp256k1_context_static.
* It is not necessary to call this function before using a context created with
* secp256k1_context_create (or secp256k1_context_preallocated_create), which will
* take care of performing the self tests.
*
* If the tests fail, this function will call the default error handler to abort the
* program (see secp256k1_context_set_error_callback).
*/
SECP256K1_API void secp256k1_selftest(void);
/** Create a secp256k1 context object (in dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see secp256k1_context_static and the functions in
* secp256k1_preallocated.h.
*
* Returns: a newly created context object.
* In: flags: Always set to SECP256K1_CONTEXT_NONE (see below).
*
* The only valid non-deprecated flag in recent library versions is
* SECP256K1_CONTEXT_NONE, which will create a context sufficient for all functionality
* offered by the library. All other (deprecated) flags will be treated as equivalent
* to the SECP256K1_CONTEXT_NONE flag. Though the flags parameter primarily exists for
* historical reasons, future versions of the library may introduce new flags.
*
* If the context is intended to be used for API functions that perform computations
* involving secret keys, e.g., signing and public key generation, then it is highly
* recommended to call secp256k1_context_randomize on the context before calling
* those API functions. This will provide enhanced protection against side-channel
* leakage, see secp256k1_context_randomize for details.
*
* Do not create a new context object for each operation, as construction and
* randomization can take non-negligible time.
*/
SECP256K1_API secp256k1_context* secp256k1_context_create(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
/** Copy a secp256k1 context object (into dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see the functions in secp256k1_preallocated.h.
*
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
* Returns: a newly created context object.
* Args: ctx: an existing context to copy (not secp256k1_context_static)
*/
SECP256K1_API secp256k1_context* secp256k1_context_clone(
const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object (created in dynamically allocated memory).
*
* The context pointer may not be used afterwards.
*
* The context to destroy must have been created using secp256k1_context_create
* or secp256k1_context_clone. If the context has instead been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the
* behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
* be used instead.
*
* Args: ctx: an existing context to destroy, constructed using
* secp256k1_context_create or secp256k1_context_clone
* (i.e., not secp256k1_context_static).
*/
SECP256K1_API void secp256k1_context_destroy(
secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1);
/** Set a callback function to be called when an illegal argument is passed to
* an API call. It will only trigger for violations that are mentioned
* explicitly in the header.
*
* The philosophy is that these shouldn't be dealt with through a
* specific return value, as calling code should not have branches to deal with
* the case that this code itself is broken.
*
* On the other hand, during debug stage, one would want to be informed about
* such mistakes, and the default (crashing) may be inadvisable.
* When this callback is triggered, the API function called is guaranteed not
* to cause a crash, though its return value and output arguments are
* undefined.
*
* When this function has not been called (or called with fn==NULL), then the
* default handler will be used. The library provides a default handler which
* writes the message to stderr and calls abort. This default handler can be
* replaced at link time if the preprocessor macro
* USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build
* has been configured with --enable-external-default-callbacks. Then the
* following two symbols must be provided to link against:
* - void secp256k1_default_illegal_callback_fn(const char* message, void* data);
* - void secp256k1_default_error_callback_fn(const char* message, void* data);
* The library can call these default handlers even before a proper callback data
* pointer could have been set using secp256k1_context_set_illegal_callback or
* secp256k1_context_set_error_callback, e.g., when the creation of a context
* fails. In this case, the corresponding default handler will be called with
* the data pointer argument set to NULL.
*
* Args: ctx: an existing context object.
* In: fun: a pointer to a function to call when an illegal argument is
* passed to the API, taking a message and an opaque pointer.
* (NULL restores the default handler.)
* data: the opaque pointer to pass to fun above, must be NULL for the default handler.
*
* See also secp256k1_context_set_error_callback.
*/
SECP256K1_API void secp256k1_context_set_illegal_callback(
secp256k1_context* ctx,
void (*fun)(const char* message, void* data),
const void* data
) SECP256K1_ARG_NONNULL(1);
/** Set a callback function to be called when an internal consistency check
* fails.
*
* The default callback writes an error message to stderr and calls abort
* to abort the program.
*
* This can only trigger in case of a hardware failure, miscompilation,
* memory corruption, serious bug in the library, or other error would can
* otherwise result in undefined behaviour. It will not trigger due to mere
* incorrect usage of the API (see secp256k1_context_set_illegal_callback
* for that). After this callback returns, anything may happen, including
* crashing.
*
* Args: ctx: an existing context object.
* In: fun: a pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores the
* default handler, see secp256k1_context_set_illegal_callback
* for details).
* data: the opaque pointer to pass to fun above, must be NULL for the default handler.
*
* See also secp256k1_context_set_illegal_callback.
*/
SECP256K1_API void secp256k1_context_set_error_callback(
secp256k1_context* ctx,
void (*fun)(const char* message, void* data),
const void* data
) SECP256K1_ARG_NONNULL(1);
/** Create a secp256k1 scratch space object.
*
* Returns: a newly created scratch space.
* Args: ctx: an existing context object.
* In: size: amount of memory to be available as scratch space. Some extra
* (<100 bytes) will be allocated for extra accounting.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
const secp256k1_context* ctx,
size_t size
) SECP256K1_ARG_NONNULL(1);
/** Destroy a secp256k1 scratch space.
*
* The pointer may not be used afterwards.
* Args: ctx: a secp256k1 context object.
* scratch: space to destroy
*/
SECP256K1_API void secp256k1_scratch_space_destroy(
const secp256k1_context* ctx,
secp256k1_scratch_space* scratch
) SECP256K1_ARG_NONNULL(1);
/** Parse a variable-length public key into the pubkey object.
*
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
* Args: ctx: a secp256k1 context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, its value is undefined.
* In: input: pointer to a serialized public key
* inputlen: length of the array pointed to by input
*
* This function supports parsing compressed (33 bytes, header byte 0x02 or
* 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header
* byte 0x06 or 0x07) format public keys.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse(
const secp256k1_context* ctx,
secp256k1_pubkey* pubkey,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize a pubkey object into a serialized byte sequence.
*
* Returns: 1 always.
* Args: ctx: a secp256k1 context object.
* Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
* compressed==1) byte array to place the serialized key
* in.
* In/Out: outputlen: a pointer to an integer which is initially set to the
* size of output, and is overwritten with the written
* size.
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key.
* flags: SECP256K1_EC_COMPRESSED if serialization should be in
* compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
*/
SECP256K1_API int secp256k1_ec_pubkey_serialize(
const secp256k1_context* ctx,
unsigned char *output,
size_t *outputlen,
const secp256k1_pubkey* pubkey,
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Compare two public keys using lexicographic (of compressed serialization) order
*
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
* Args: ctx: a secp256k1 context object.
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
const secp256k1_context* ctx,
const secp256k1_pubkey* pubkey1,
const secp256k1_pubkey* pubkey2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Parse an ECDSA signature in compact (64 bytes) format.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input64: a pointer to the 64-byte array to parse
*
* The signature must consist of a 32-byte big endian R value, followed by a
* 32-byte big endian S value. If R or S fall outside of [0..order-1], the
* encoding is invalid. R and S with value 0 are allowed in the encoding.
*
* After the call, sig will always be initialized. If parsing failed or R or
* S are zero, the resulting sig value is guaranteed to fail verification for
* any message and public key.
*/
SECP256K1_API int secp256k1_ecdsa_signature_parse_compact(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature* sig,
const unsigned char *input64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Parse a DER ECDSA signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input: a pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
* encoded numbers are out of range.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature verification with it is
* guaranteed to fail for every message and public key.
*/
SECP256K1_API int secp256k1_ecdsa_signature_parse_der(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature* sig,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an ECDSA signature in DER format.
*
* Returns: 1 if enough space was available to serialize, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: output: a pointer to an array to store the DER serialization
* In/Out: outputlen: a pointer to a length integer. Initially, this integer
* should be set to the length of output. After the call
* it will be set to the length of the serialization (even
* if 0 was returned).
* In: sig: a pointer to an initialized signature object
*/
SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
const secp256k1_context* ctx,
unsigned char *output,
size_t *outputlen,
const secp256k1_ecdsa_signature* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Serialize an ECDSA signature in compact (64 byte) format.
*
* Returns: 1
* Args: ctx: a secp256k1 context object
* Out: output64: a pointer to a 64-byte array to store the compact serialization
* In: sig: a pointer to an initialized signature object
*
* See secp256k1_ecdsa_signature_parse_compact for details about the encoding.
*/
SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
const secp256k1_context* ctx,
unsigned char *output64,
const secp256k1_ecdsa_signature* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Verify an ECDSA signature.
*
* Returns: 1: correct signature
* 0: incorrect or unparseable signature
* Args: ctx: a secp256k1 context object.
* In: sig: the signature being verified.
* msghash32: the 32-byte message hash being verified.
* The verifier must make sure to apply a cryptographic
* hash function to the message by itself and not accept an
* msghash32 value directly. Otherwise, it would be easy to
* create a "valid" signature without knowledge of the
* secret key. See also
* https://bitcoin.stackexchange.com/a/81116/35586 for more
* background on this topic.
* pubkey: pointer to an initialized public key to verify with.
*
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
* form are accepted.
*
* If you need to accept ECDSA signatures from sources that do not obey this
* rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to
* verification, but be aware that doing so results in malleable signatures.
*
* For details, see the comments for that function.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context* ctx,
const secp256k1_ecdsa_signature *sig,
const unsigned char *msghash32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Convert a signature to a normalized lower-S form.
*
* Returns: 1 if sigin was not normalized, 0 if it already was.
* Args: ctx: a secp256k1 context object
* Out: sigout: a pointer to a signature to fill with the normalized form,
* or copy if the input was already normalized. (can be NULL if
* you're only interested in whether the input was already
* normalized).
* In: sigin: a pointer to a signature to check/normalize (can be identical to sigout)
*
* With ECDSA a third-party can forge a second distinct signature of the same
* message, given a single initial signature, but without knowing the key. This
* is done by negating the S value modulo the order of the curve, 'flipping'
* the sign of the random point R which is not included in the signature.
*
* Forgery of the same message isn't universally problematic, but in systems
* where message malleability or uniqueness of signatures is important this can
* cause issues. This forgery can be blocked by all verifiers forcing signers
* to use a normalized form.
*
* The lower-S form reduces the size of signatures slightly on average when
* variable length encodings (such as DER) are used and is cheap to verify,
* making it a good choice. Security of always using lower-S is assured because
* anyone can trivially modify a signature after the fact to enforce this
* property anyway.
*
* The lower S value is always between 0x1 and
* 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
* inclusive.
*
* No other forms of ECDSA malleability are known and none seem likely, but
* there is no formal proof that ECDSA, even with this additional restriction,
* is free of other malleability. Commonly used serialization schemes will also
* accept various non-unique encodings, so care should be taken when this
* property is required for an application.
*
* The secp256k1_ecdsa_sign function will by default create signatures in the
* lower-S form, and secp256k1_ecdsa_verify will not accept others. In case
* signatures come from a system that cannot enforce this property,
* secp256k1_ecdsa_signature_normalize must be called before verification.
*/
SECP256K1_API int secp256k1_ecdsa_signature_normalize(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sigout,
const secp256k1_ecdsa_signature *sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* extra entropy.
*/
SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
SECP256K1_API_VAR const secp256k1_nonce_function secp256k1_nonce_function_default;
/** Create an ECDSA signature.
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig: pointer to an array where the signature will be placed.
* In: msghash32: the 32-byte message hash being signed.
* seckey: pointer to a 32-byte secret key.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_default is used.
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_default is used, then ndata must be a
* pointer to 32-bytes of additional data.
*
* The created signature is always in lower-S form. See
* secp256k1_ecdsa_signature_normalize for more details.
*/
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sig,
const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Verify an ECDSA secret key.
*
* A secret key is valid if it is not 0 and less than the secp256k1 curve order
* when interpreted as an integer (most significant byte first). The
* probability of choosing a 32-byte string uniformly at random which is an
* invalid secret key is negligible.
*
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(
const secp256k1_context* ctx,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Compute the public key for a secret key.
*
* Returns: 1: secret was valid, public key stores.
* 0: secret was invalid, try again.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: pubkey: pointer to the created public key.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Negates a secret key in place.
*
* Returns: 0 if the given secret key is invalid according to
* secp256k1_ec_seckey_verify. 1 otherwise
* Args: ctx: pointer to a context object
* In/Out: seckey: pointer to the 32-byte secret key to be negated. If the
* secret key is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0 and
* seckey will be set to some unspecified value.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate(
const secp256k1_context* ctx,
unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in
* future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
const secp256k1_context* ctx,
unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2)
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_negate instead");
/** Negates a public key in place.
*
* Returns: 1 always
* Args: ctx: pointer to a context object
* In/Out: pubkey: pointer to the public key to be negated.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
/** Tweak a secret key by adding tweak to it.
*
* Returns: 0 if the arguments are invalid or the resulting secret key would be
* invalid (only when the tweak is the negation of the secret key). 1
* otherwise.
* Args: ctx: pointer to a context object.
* In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
* invalid according to secp256k1_ec_seckey_verify, this
* function returns 0. seckey will be set to some unspecified
* value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in
* future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_add instead");
/** Tweak a public key by adding tweak times the generator to it.
*
* Returns: 0 if the arguments are invalid or the resulting public key would be
* invalid (only when the tweak is the negation of the corresponding
* secret key). 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Tweak a secret key by multiplying it by a tweak.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: seckey: pointer to a 32-byte secret key. If the secret key is
* invalid according to secp256k1_ec_seckey_verify, this
* function returns 0. seckey will be set to some unspecified
* value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in
* future versions. */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_mul instead");
/** Tweak a public key by multiplying it by a tweak value.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object.
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
* invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to
* secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Randomizes the context to provide enhanced protection against side-channel leakage.
*
* Returns: 1: randomization successful
* 0: error
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state).
*
* While secp256k1 code is written and tested to be constant-time no matter what
* secret values are, it is possible that a compiler may output code which is not,
* and also that the CPU may not emit the same radio frequencies or draw the same
* amount of power for all values. Randomization of the context shields against
* side-channel observations which aim to exploit secret-dependent behaviour in
* certain computations which involve secret keys.
*
* It is highly recommended to call this function on contexts returned from
* secp256k1_context_create or secp256k1_context_clone (or from the corresponding
* functions in secp256k1_preallocated.h) before using these contexts to call API
* functions that perform computations involving secret keys, e.g., signing and
* public key generation. It is possible to call this function more than once on
* the same context, and doing so before every few computations involving secret
* keys is recommended as a defense-in-depth measure. Randomization of the static
* context secp256k1_context_static is not supported.
*
* Currently, the random seed is mainly used for blinding multiplications of a
* secret scalar with the elliptic curve base point. Multiplications of this
* kind are performed by exactly those API functions which are documented to
* require a context that is not secp256k1_context_static. As a rule of thumb,
* these are all functions which take a secret key (or a keypair) as an input.
* A notable exception to that rule is the ECDH module, which relies on a different
* kind of elliptic curve point multiplication and thus does not benefit from
* enhanced protection against side-channel leakage currently.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
secp256k1_context* ctx,
const unsigned char *seed32
) SECP256K1_ARG_NONNULL(1);
/** Add a number of public keys together.
*
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object.
* Out: out: pointer to a public key object for placing the resulting public key.
* In: ins: pointer to array of pointers to public keys.
* n: the number of public keys to add together (must be at least 1).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
const secp256k1_context* ctx,
secp256k1_pubkey *out,
const secp256k1_pubkey * const * ins,
size_t n
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Compute a tagged hash as defined in BIP-340.
*
* This is useful for creating a message hash and achieving domain separation
* through an application-specific tag. This function returns
* SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash
* implementations optimized for a specific tag can precompute the SHA256 state
* after hashing the tag hashes.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object
* Out: hash32: pointer to a 32-byte array to store the resulting hash
* In: tag: pointer to an array containing the tag
* taglen: length of the tag array
* msg: pointer to an array containing the message
* msglen: length of the message array
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256(
const secp256k1_context* ctx,
unsigned char *hash32,
const unsigned char *tag,
size_t taglen,
const unsigned char *msg,
size_t msglen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_H */

View file

@ -0,0 +1,63 @@
#ifndef SECP256K1_ECDH_H
#define SECP256K1_ECDH_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** A pointer to a function that hashes an EC point to obtain an ECDH secret
*
* Returns: 1 if the point was successfully hashed.
* 0 will cause secp256k1_ecdh to fail and return 0.
* Other return values are not allowed, and the behaviour of
* secp256k1_ecdh is undefined for other return values.
* Out: output: pointer to an array to be filled by the function
* In: x32: pointer to a 32-byte x coordinate
* y32: pointer to a 32-byte y coordinate
* data: arbitrary data pointer that is passed through
*/
typedef int (*secp256k1_ecdh_hash_function)(
unsigned char *output,
const unsigned char *x32,
const unsigned char *y32,
void *data
);
/** An implementation of SHA256 hash function that applies to compressed public key.
* Populates the output parameter with 32 bytes. */
SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
/** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256).
* Populates the output parameter with 32 bytes. */
SECP256K1_API_VAR const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default;
/** Compute an EC Diffie-Hellman secret in constant time
*
* Returns: 1: exponentiation was successful
* 0: scalar was invalid (zero or overflow) or hashfp returned 0
* Args: ctx: pointer to a context object.
* Out: output: pointer to an array to be filled by hashfp.
* In: pubkey: a pointer to a secp256k1_pubkey containing an initialized public key.
* seckey: a 32-byte scalar with which to multiply the point.
* hashfp: pointer to a hash function. If NULL,
* secp256k1_ecdh_hash_function_sha256 is used
* (in which case, 32 bytes will be written to output).
* data: arbitrary data pointer that is passed through to hashfp
* (can be NULL for secp256k1_ecdh_hash_function_sha256).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
const secp256k1_context* ctx,
unsigned char *output,
const secp256k1_pubkey *pubkey,
const unsigned char *seckey,
secp256k1_ecdh_hash_function hashfp,
void *data
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_ECDH_H */

View file

@ -0,0 +1,249 @@
#ifndef SECP256K1_EXTRAKEYS_H
#define SECP256K1_EXTRAKEYS_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Opaque data structure that holds a parsed and valid "x-only" public key.
* An x-only pubkey encodes a point whose Y coordinate is even. It is
* serialized using only its X coordinate (32 bytes). See BIP-340 for more
* information about x-only pubkeys.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage, transmission, use
* use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To
* compare keys, use secp256k1_xonly_pubkey_cmp.
*/
typedef struct {
unsigned char data[64];
} secp256k1_xonly_pubkey;
/** Opaque data structure that holds a keypair consisting of a secret and a
* public key.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 96 bytes in size, and can be safely copied/moved.
*/
typedef struct {
unsigned char data[96];
} secp256k1_keypair;
/** Parse a 32-byte sequence into a xonly_pubkey object.
*
* Returns: 1 if the public key was fully valid.
* 0 if the public key could not be parsed or is invalid.
*
* Args: ctx: a secp256k1 context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
* parsed version of input. If not, it's set to an invalid value.
* In: input32: pointer to a serialized xonly_pubkey.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse(
const secp256k1_context* ctx,
secp256k1_xonly_pubkey* pubkey,
const unsigned char *input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an xonly_pubkey object into a 32-byte sequence.
*
* Returns: 1 always.
*
* Args: ctx: a secp256k1 context object.
* Out: output32: a pointer to a 32-byte array to place the serialized key in.
* In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an initialized public key.
*/
SECP256K1_API int secp256k1_xonly_pubkey_serialize(
const secp256k1_context* ctx,
unsigned char *output32,
const secp256k1_xonly_pubkey* pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Compare two x-only public keys using lexicographic order
*
* Returns: <0 if the first public key is less than the second
* >0 if the first public key is greater than the second
* 0 if the two public keys are equal
* Args: ctx: a secp256k1 context object.
* In: pubkey1: first public key to compare
* pubkey2: second public key to compare
*/
SECP256K1_API int secp256k1_xonly_pubkey_cmp(
const secp256k1_context* ctx,
const secp256k1_xonly_pubkey* pk1,
const secp256k1_xonly_pubkey* pk2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
*
* Returns: 1 always.
*
* Args: ctx: pointer to a context object.
* Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key.
* pk_parity: Ignored if NULL. Otherwise, pointer to an integer that
* will be set to 1 if the point encoded by xonly_pubkey is
* the negation of the pubkey and set to 0 otherwise.
* In: pubkey: pointer to a public key that is converted.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey(
const secp256k1_context* ctx,
secp256k1_xonly_pubkey *xonly_pubkey,
int *pk_parity,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
/** Tweak an x-only public key by adding the generator multiplied with tweak32
* to it.
*
* Note that the resulting point can not in general be represented by an x-only
* pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
* is a normal secp256k1_pubkey.
*
* Returns: 0 if the arguments are invalid or the resulting public key would be
* invalid (only when the tweak is the negation of the corresponding
* secret key). 1 otherwise.
*
* Args: ctx: pointer to a context object.
* Out: output_pubkey: pointer to a public key to store the result. Will be set
* to an invalid value if this function returns 0.
* In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
* tweak32: pointer to a 32-byte tweak. If the tweak is invalid
* according to secp256k1_ec_seckey_verify, this function
* returns 0. For uniformly random 32-byte arrays the
* chance of being invalid is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
const secp256k1_context* ctx,
secp256k1_pubkey *output_pubkey,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Checks that a tweaked pubkey is the result of calling
* secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
*
* The tweaked pubkey is represented by its 32-byte x-only serialization and
* its pk_parity, which can both be obtained by converting the result of
* tweak_add to a secp256k1_xonly_pubkey.
*
* Note that this alone does _not_ verify that the tweaked pubkey is a
* commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
* can easily be the result of a different internal_pubkey and tweak.
*
* Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
* result of tweaking the internal_pubkey with tweak32. 1 otherwise.
* Args: ctx: pointer to a context object.
* In: tweaked_pubkey32: pointer to a serialized xonly_pubkey.
* tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
* is passed in as tweaked_pubkey32). This must match the
* pk_parity value that is returned when calling
* secp256k1_xonly_pubkey with the tweaked pubkey, or
* this function will fail.
* internal_pubkey: pointer to an x-only public key object to apply the tweak to.
* tweak32: pointer to a 32-byte tweak.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check(
const secp256k1_context* ctx,
const unsigned char *tweaked_pubkey32,
int tweaked_pk_parity,
const secp256k1_xonly_pubkey *internal_pubkey,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
/** Compute the keypair for a secret key.
*
* Returns: 1: secret was valid, keypair is ready to use
* 0: secret was invalid, try again with a different secret
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: keypair: pointer to the created keypair.
* In: seckey: pointer to a 32-byte secret key.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
const secp256k1_context* ctx,
secp256k1_keypair *keypair,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the secret key from a keypair.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: seckey: pointer to a 32-byte buffer for the secret key.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec(
const secp256k1_context* ctx,
unsigned char *seckey,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the public key from a keypair.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
* the keypair public key. If not, it's set to an invalid value.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Get the x-only public key from a keypair.
*
* This is the same as calling secp256k1_keypair_pub and then
* secp256k1_xonly_pubkey_from_pubkey.
*
* Returns: 1 always.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
* to the keypair public key after converting it to an
* xonly_pubkey. If not, it's set to an invalid value.
* pk_parity: Ignored if NULL. Otherwise, pointer to an integer that will be set to the
* pk_parity argument of secp256k1_xonly_pubkey_from_pubkey.
* In: keypair: pointer to a keypair.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
const secp256k1_context* ctx,
secp256k1_xonly_pubkey *pubkey,
int *pk_parity,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
/** Tweak a keypair by adding tweak32 to the secret key and updating the public
* key accordingly.
*
* Calling this function and then secp256k1_keypair_pub results in the same
* public key as calling secp256k1_keypair_xonly_pub and then
* secp256k1_xonly_pubkey_tweak_add.
*
* Returns: 0 if the arguments are invalid or the resulting keypair would be
* invalid (only when the tweak is the negation of the keypair's
* secret key). 1 otherwise.
*
* Args: ctx: pointer to a context object.
* In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
* an invalid value if this function returns 0.
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
* to secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
const secp256k1_context* ctx,
secp256k1_keypair *keypair,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_EXTRAKEYS_H */

View file

@ -0,0 +1,134 @@
#ifndef SECP256K1_PREALLOCATED_H
#define SECP256K1_PREALLOCATED_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The module provided by this header file is intended for settings in which it
* is not possible or desirable to rely on dynamic memory allocation. It provides
* functions for creating, cloning, and destroying secp256k1 context objects in a
* contiguous fixed-size block of memory provided by the caller.
*
* Context objects created by functions in this module can be used like contexts
* objects created by functions in secp256k1.h, i.e., they can be passed to any
* API function that expects a context object (see secp256k1.h for details). The
* only exception is that context objects created by functions in this module
* must be destroyed using secp256k1_context_preallocated_destroy (in this
* module) instead of secp256k1_context_destroy (in secp256k1.h).
*
* It is guaranteed that functions in this module will not call malloc or its
* friends realloc, calloc, and free.
*/
/** Determine the memory size of a secp256k1 context object to be created in
* caller-provided memory.
*
* The purpose of this function is to determine how much memory must be provided
* to secp256k1_context_preallocated_create.
*
* Returns: the required size of the caller-provided memory block
* In: flags: which parts of the context to initialize.
*/
SECP256K1_API size_t secp256k1_context_preallocated_size(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
/** Create a secp256k1 context object in caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, which begins with the call to this
* function and ends when a call to secp256k1_context_preallocated_destroy
* (which destroys the context object again) returns. During the lifetime of the
* context object, the caller is obligated not to access this block of memory,
* i.e., the caller may not read or write the memory, e.g., by copying the memory
* contents to a different location or trying to create a second context object
* in the memory. In simpler words, the prealloc pointer (or any pointer derived
* from it) should not be used during the lifetime of the context object.
*
* Returns: a newly created context object.
* In: prealloc: a pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
* flags: which parts of the context to initialize.
*
* See secp256k1_context_create (in secp256k1.h) for further details.
*
* See also secp256k1_context_randomize (in secp256k1.h)
* and secp256k1_context_preallocated_destroy.
*/
SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create(
void* prealloc,
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Determine the memory size of a secp256k1 context object to be copied into
* caller-provided memory.
*
* Returns: the required size of the caller-provided memory block.
* In: ctx: an existing context to copy.
*/
SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Copy a secp256k1 context object into caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, see the description of
* secp256k1_context_preallocated_create for details.
*
* Cloning secp256k1_context_static is not possible, and should not be emulated by
* the caller (e.g., using memcpy). Create a new context instead.
*
* Returns: a newly created context object.
* Args: ctx: an existing context to copy (not secp256k1_context_static).
* In: prealloc: a pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above.
*/
SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone(
const secp256k1_context* ctx,
void* prealloc
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object that has been created in
* caller-provided memory.
*
* The context pointer may not be used afterwards.
*
* The context to destroy must have been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone.
* If the context has instead been created using secp256k1_context_create or
* secp256k1_context_clone, the behaviour is undefined. In that case,
* secp256k1_context_destroy must be used instead.
*
* If required, it is the responsibility of the caller to deallocate the block
* of memory properly after this function returns, e.g., by calling free on the
* preallocated pointer given to secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone.
*
* Args: ctx: an existing context to destroy, constructed using
* secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone
* (i.e., not secp256k1_context_static).
*/
SECP256K1_API void secp256k1_context_preallocated_destroy(
secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_PREALLOCATED_H */

View file

@ -0,0 +1,113 @@
#ifndef SECP256K1_RECOVERY_H
#define SECP256K1_RECOVERY_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Opaque data structured that holds a parsed ECDSA signature,
* supporting pubkey recovery.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 65 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage or transmission, use
* the secp256k1_ecdsa_signature_serialize_* and
* secp256k1_ecdsa_signature_parse_* functions.
*
* Furthermore, it is guaranteed that identical signatures (including their
* recoverability) will have identical representation, so they can be
* memcmp'ed.
*/
typedef struct {
unsigned char data[65];
} secp256k1_ecdsa_recoverable_signature;
/** Parse a compact ECDSA signature (64 bytes + recovery id).
*
* Returns: 1 when the signature could be parsed, 0 otherwise
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input64: a pointer to a 64-byte compact signature
* recid: the recovery id (0, 1, 2 or 3)
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact(
const secp256k1_context* ctx,
secp256k1_ecdsa_recoverable_signature* sig,
const unsigned char *input64,
int recid
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Convert a recoverable signature into a normal signature.
*
* Returns: 1
* Args: ctx: a secp256k1 context object.
* Out: sig: a pointer to a normal signature.
* In: sigin: a pointer to a recoverable signature.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature* sig,
const secp256k1_ecdsa_recoverable_signature* sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Serialize an ECDSA signature in compact format (64 bytes + recovery id).
*
* Returns: 1
* Args: ctx: a secp256k1 context object.
* Out: output64: a pointer to a 64-byte array of the compact signature.
* recid: a pointer to an integer to hold the recovery id.
* In: sig: a pointer to an initialized signature object.
*/
SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
const secp256k1_context* ctx,
unsigned char *output64,
int *recid,
const secp256k1_ecdsa_recoverable_signature* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Create a recoverable ECDSA signature.
*
* Returns: 1: signature created
* 0: the nonce generation function failed, or the secret key was invalid.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig: pointer to an array where the signature will be placed.
* In: msghash32: the 32-byte message hash being signed.
* seckey: pointer to a 32-byte secret key.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_default is used.
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL for secp256k1_nonce_function_default).
*/
SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
const secp256k1_context* ctx,
secp256k1_ecdsa_recoverable_signature *sig,
const unsigned char *msghash32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Recover an ECDSA public key from a signature.
*
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
* Args: ctx: pointer to a context object.
* Out: pubkey: pointer to the recovered public key.
* In: sig: pointer to initialized signature that supports pubkey recovery.
* msghash32: the 32-byte message hash assumed to be signed.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const secp256k1_ecdsa_recoverable_signature *sig,
const unsigned char *msghash32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_RECOVERY_H */

View file

@ -0,0 +1,182 @@
#ifndef SECP256K1_SCHNORRSIG_H
#define SECP256K1_SCHNORRSIG_H
#include "secp256k1.h"
#include "secp256k1_extrakeys.h"
#ifdef __cplusplus
extern "C" {
#endif
/** This module implements a variant of Schnorr signatures compliant with
* Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1"
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
*/
/** A pointer to a function to deterministically generate a nonce.
*
* Same as secp256k1_nonce function with the exception of accepting an
* additional pubkey argument and not requiring an attempt argument. The pubkey
* argument can protect signature schemes with key-prefixed challenge hash
* inputs against reusing the nonce when signing with the wrong precomputed
* pubkey.
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to
* return an error.
* Out: nonce32: pointer to a 32-byte array to be filled by the function
* In: msg: the message being verified. Is NULL if and only if msglen
* is 0.
* msglen: the length of the message
* key32: pointer to a 32-byte secret key (will not be NULL)
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
* (will not be NULL)
* algo: pointer to an array describing the signature
* algorithm (will not be NULL)
* algolen: the length of the algo array
* data: arbitrary data pointer that is passed through
*
* Except for test cases, this function should compute some cryptographic hash of
* the message, the key, the pubkey, the algorithm description, and data.
*/
typedef int (*secp256k1_nonce_function_hardened)(
unsigned char *nonce32,
const unsigned char *msg,
size_t msglen,
const unsigned char *key32,
const unsigned char *xonly_pk32,
const unsigned char *algo,
size_t algolen,
void *data
);
/** An implementation of the nonce generation function as defined in Bitcoin
* Improvement Proposal 340 "Schnorr Signatures for secp256k1"
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
*
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* auxiliary random data as defined in BIP-340. If the data pointer is NULL,
* the nonce derivation procedure follows BIP-340 by setting the auxiliary
* random data to zero. The algo argument must be non-NULL, otherwise the
* function will fail and return 0. The hash will be tagged with algo.
* Therefore, to create BIP-340 compliant signatures, algo must be set to
* "BIP0340/nonce" and algolen to 13.
*/
SECP256K1_API_VAR const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
/** Data structure that contains additional arguments for schnorrsig_sign_custom.
*
* A schnorrsig_extraparams structure object can be initialized correctly by
* setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
*
* Members:
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
* and has no other function than making sure the object is
* initialized.
* noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_bip340 is used
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_bip340 is used, then ndata must be a
* pointer to 32-byte auxiliary randomness as per BIP-340.
*/
typedef struct {
unsigned char magic[4];
secp256k1_nonce_function_hardened noncefp;
void* ndata;
} secp256k1_schnorrsig_extraparams;
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c }
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
NULL,\
NULL\
}
/** Create a Schnorr signature.
*
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
* signature. Instead, you can manually use secp256k1_schnorrsig_verify and
* abort if it fails.
*
* This function only signs 32-byte messages. If you have messages of a
* different size (or the same size but without a context-specific tag
* prefix), it is recommended to create a 32-byte message hash with
* secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
* providing an context-specific tag for domain separation. This prevents
* signatures from being valid in multiple contexts by accident.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (not secp256k1_context_static).
* Out: sig64: pointer to a 64-byte array to store the serialized signature.
* In: msg32: the 32-byte message being signed.
* keypair: pointer to an initialized keypair.
* aux_rand32: 32 bytes of fresh randomness. While recommended to provide
* this, it is only supplemental to security and can be NULL. A
* NULL argument is treated the same as an all-zero one. See
* BIP-340 "Default Signing" for a full explanation of this
* argument and for guidance if randomness is expensive.
*/
SECP256K1_API int secp256k1_schnorrsig_sign32(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_keypair *keypair,
const unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
/** Same as secp256k1_schnorrsig_sign32, but DEPRECATED. Will be removed in
* future versions. */
SECP256K1_API int secp256k1_schnorrsig_sign(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_keypair *keypair,
const unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
SECP256K1_DEPRECATED("Use secp256k1_schnorrsig_sign32 instead");
/** Create a Schnorr signature with a more flexible API.
*
* Same arguments as secp256k1_schnorrsig_sign except that it allows signing
* variable length messages and accepts a pointer to an extraparams object that
* allows customizing signing by passing additional arguments.
*
* Creates the same signatures as schnorrsig_sign if msglen is 32 and the
* extraparams.ndata is the same as aux_rand32.
*
* In: msg: the message being signed. Can only be NULL if msglen is 0.
* msglen: length of the message
* extraparams: pointer to a extraparams object (can be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_sign_custom(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_keypair *keypair,
secp256k1_schnorrsig_extraparams *extraparams
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
/** Verify a Schnorr signature.
*
* Returns: 1: correct signature
* 0: incorrect signature
* Args: ctx: a secp256k1 context object.
* In: sig64: pointer to the 64-byte signature to verify.
* msg: the message being verified. Can only be NULL if msglen is 0.
* msglen: length of the message
* pubkey: pointer to an x-only public key to verify with (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context* ctx,
const unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_xonly_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_SCHNORRSIG_H */

View file

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libsecp256k1
Description: Optimized C library for EC operations on curve secp256k1
URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -lsecp256k1

View file

@ -0,0 +1,156 @@
load("secp256k1_params.sage")
MAX_ORDER = 1000
# Set of (curve) orders we have encountered so far.
orders_done = set()
# Map from (subgroup) orders to [b, int(gen.x), int(gen.y), gen, lambda] for those subgroups.
solutions = {}
# Iterate over curves of the form y^2 = x^3 + B.
for b in range(1, P):
# There are only 6 curves (up to isomorphism) of the form y^2 = x^3 + B. Stop once we have tried all.
if len(orders_done) == 6:
break
E = EllipticCurve(F, [0, b])
print("Analyzing curve y^2 = x^3 + %i" % b)
n = E.order()
# Skip curves with an order we've already tried
if n in orders_done:
print("- Isomorphic to earlier curve")
print()
continue
orders_done.add(n)
# Skip curves isomorphic to the real secp256k1
if n.is_pseudoprime():
assert E.is_isomorphic(C)
print("- Isomorphic to secp256k1")
print()
continue
print("- Finding prime subgroups")
# Map from group_order to a set of independent generators for that order.
curve_gens = {}
for g in E.gens():
# Find what prime subgroups of group generated by g exist.
g_order = g.order()
for f, _ in g.order().factor():
# Skip subgroups that have bad size.
if f < 4:
print(f" - Subgroup of size {f}: too small")
continue
if f > MAX_ORDER:
print(f" - Subgroup of size {f}: too large")
continue
# Construct a generator for that subgroup.
gen = g * (g_order // f)
assert(gen.order() == f)
# Add to set the minimal multiple of gen.
curve_gens.setdefault(f, set()).add(min([j*gen for j in range(1, f)]))
print(f" - Subgroup of size {f}: ok")
for f in sorted(curve_gens.keys()):
print(f"- Constructing group of order {f}")
cbrts = sorted([int(c) for c in Integers(f)(1).nth_root(3, all=true) if c != 1])
gens = list(curve_gens[f])
sol_count = 0
no_endo_count = 0
# Consider all non-zero linear combinations of the independent generators.
for j in range(1, f**len(gens)):
gen = sum(gens[k] * ((j // f**k) % f) for k in range(len(gens)))
assert not gen.is_zero()
assert (f*gen).is_zero()
# Find lambda for endomorphism. Skip if none can be found.
lam = None
for l in cbrts:
if l*gen == E(BETA*gen[0], gen[1]):
lam = l
break
if lam is None:
no_endo_count += 1
else:
sol_count += 1
solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam))
print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)")
print()
def output_generator(g, name):
print(f"#define {name} SECP256K1_GE_CONST(\\")
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
print(")")
def output_b(b):
print(f"#define SECP256K1_B {int(b)}")
print()
print("To be put in src/group_impl.h:")
print()
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
for f in sorted(solutions.keys()):
# Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers).
b, _, _, HALF_G, lam = min(solutions[f])
output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}")
print("/** Generator for secp256k1, value 'g' defined in")
print(" * \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.")
print(" */")
output_generator(G, "SECP256K1_G")
print("/* These exhaustive group test orders and generators are chosen such that:")
print(" * - The field size is equal to that of secp256k1, so field code is the same.")
print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.")
print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.")
print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.")
print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).")
print(" */")
print("#if defined(EXHAUSTIVE_TEST_ORDER)")
first = True
for f in sorted(solutions.keys()):
b, _, _, _, lam = min(solutions[f])
print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}")
first = False
print()
print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};")
output_b(b)
print()
print("# else")
print("# error No known generator for the specified exhaustive test group order.")
print("# endif")
print("#else")
print()
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;")
output_b(7)
print()
print("#endif")
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")
print()
print()
print("To be put in src/scalar_impl.h:")
print()
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
first = True
for f in sorted(solutions.keys()):
_, _, _, _, lam = min(solutions[f])
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
first = False
print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam)
print("# else")
print("# error No known lambda for the specified exhaustive test group order.")
print("# endif")
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")

View file

@ -0,0 +1,114 @@
""" Generates the constants used in secp256k1_scalar_split_lambda.
See the comments for secp256k1_scalar_split_lambda in src/scalar_impl.h for detailed explanations.
"""
load("secp256k1_params.sage")
def inf_norm(v):
"""Returns the infinity norm of a vector."""
return max(map(abs, v))
def gauss_reduction(i1, i2):
v1, v2 = i1.copy(), i2.copy()
while True:
if inf_norm(v2) < inf_norm(v1):
v1, v2 = v2, v1
# This is essentially
# m = round((v1[0]*v2[0] + v1[1]*v2[1]) / (inf_norm(v1)**2))
# (rounding to the nearest integer) without relying on floating point arithmetic.
m = ((v1[0]*v2[0] + v1[1]*v2[1]) + (inf_norm(v1)**2) // 2) // (inf_norm(v1)**2)
if m == 0:
return v1, v2
v2[0] -= m*v1[0]
v2[1] -= m*v1[1]
def find_split_constants_gauss():
"""Find constants for secp256k1_scalar_split_lamdba using gauss reduction."""
(v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)])
# We use related vectors in secp256k1_scalar_split_lambda.
A1, B1 = -v21, -v11
A2, B2 = v22, -v21
return A1, B1, A2, B2
def find_split_constants_explicit_tof():
"""Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius.
See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on
elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2
"""
assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10].
assert C.j_invariant() == 0
t = C.trace_of_frobenius()
c = Integer(sqrt((4*P - t**2)/3))
A1 = Integer((t - c)/2 - 1)
B1 = c
A2 = Integer((t + c)/2 - 1)
B2 = Integer(1 - (t - c)/2)
# We use a negated b values in secp256k1_scalar_split_lambda.
B1, B2 = -B1, -B2
return A1, B1, A2, B2
A1, B1, A2, B2 = find_split_constants_explicit_tof()
# For extra fun, use an independent method to recompute the constants.
assert (A1, B1, A2, B2) == find_split_constants_gauss()
# PHI : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n.
def PHI(a,b):
return Z(a + LAMBDA*b)
# Check that (A1, B1) and (A2, B2) are in the kernel of PHI.
assert PHI(A1, B1) == Z(0)
assert PHI(A2, B2) == Z(0)
# Check that the parallelogram generated by (A1, A2) and (B1, B2)
# is a fundamental domain by containing exactly N points.
# Since the LHS is the determinant and N != 0, this also checks that
# (A1, A2) and (B1, B2) are linearly independent. By the previous
# assertions, (A1, A2) and (B1, B2) are a basis of the kernel.
assert A1*B2 - B1*A2 == N
# Check that their components are short enough.
assert (A1 + A2)/2 < sqrt(N)
assert B1 < sqrt(N)
assert B2 < sqrt(N)
G1 = round((2**384)*B2/N)
G2 = round((2**384)*(-B1)/N)
def rnddiv2(v):
if v & 1:
v += 1
return v >> 1
def scalar_lambda_split(k):
"""Equivalent to secp256k1_scalar_lambda_split()."""
c1 = rnddiv2((k * G1) >> 383)
c2 = rnddiv2((k * G2) >> 383)
c1 = (c1 * -B1) % N
c2 = (c2 * -B2) % N
r2 = (c1 + c2) % N
r1 = (k + r2 * -LAMBDA) % N
return (r1, r2)
# The result of scalar_lambda_split can depend on the representation of k (mod n).
SPECIAL = (2**383) // G2 + 1
assert scalar_lambda_split(SPECIAL) != scalar_lambda_split(SPECIAL + N)
print(' A1 =', hex(A1))
print(' -B1 =', hex(-B1))
print(' A2 =', hex(A2))
print(' -B2 =', hex(-B2))
print(' =', hex(Z(-B2)))
print(' -LAMBDA =', hex(-LAMBDA))
print(' G1 =', hex(G1))
print(' G2 =', hex(G2))

View file

@ -0,0 +1,353 @@
# This code supports verifying group implementations which have branches
# or conditional statements (like cmovs), by allowing each execution path
# to independently set assumptions on input or intermediary variables.
#
# The general approach is:
# * A constraint is a tuple of two sets of symbolic expressions:
# the first of which are required to evaluate to zero, the second of which
# are required to evaluate to nonzero.
# - A constraint is said to be conflicting if any of its nonzero expressions
# is in the ideal with basis the zero expressions (in other words: when the
# zero expressions imply that one of the nonzero expressions are zero).
# * There is a list of laws that describe the intended behaviour, including
# laws for addition and doubling. Each law is called with the symbolic point
# coordinates as arguments, and returns:
# - A constraint describing the assumptions under which it is applicable,
# called "assumeLaw"
# - A constraint describing the requirements of the law, called "require"
# * Implementations are transliterated into functions that operate as well on
# algebraic input points, and are called once per combination of branches
# executed. Each execution returns:
# - A constraint describing the assumptions this implementation requires
# (such as Z1=1), called "assumeFormula"
# - A constraint describing the assumptions this specific branch requires,
# but which is by construction guaranteed to cover the entire space by
# merging the results from all branches, called "assumeBranch"
# - The result of the computation
# * All combinations of laws with implementation branches are tried, and:
# - If the combination of assumeLaw, assumeFormula, and assumeBranch results
# in a conflict, it means this law does not apply to this branch, and it is
# skipped.
# - For others, we try to prove the require constraints hold, assuming the
# information in assumeLaw + assumeFormula + assumeBranch, and if this does
# not succeed, we fail.
# + To prove an expression is zero, we check whether it belongs to the
# ideal with the assumed zero expressions as basis. This test is exact.
# + To prove an expression is nonzero, we check whether each of its
# factors is contained in the set of nonzero assumptions' factors.
# This test is not exact, so various combinations of original and
# reduced expressions' factors are tried.
# - If we succeed, we print out the assumptions from assumeFormula that
# weren't implied by assumeLaw already. Those from assumeBranch are skipped,
# as we assume that all constraints in it are complementary with each other.
#
# Based on the sage verification scripts used in the Explicit-Formulas Database
# by Tanja Lange and others, see https://hyperelliptic.org/EFD
class fastfrac:
"""Fractions over rings."""
def __init__(self,R,top,bot=1):
"""Construct a fractional, given a ring, a numerator, and denominator."""
self.R = R
if parent(top) == ZZ or parent(top) == R:
self.top = R(top)
self.bot = R(bot)
elif top.__class__ == fastfrac:
self.top = top.top
self.bot = top.bot * bot
else:
self.top = R(numerator(top))
self.bot = R(denominator(top)) * bot
def iszero(self,I):
"""Return whether this fraction is zero given an ideal."""
return self.top in I and self.bot not in I
def reduce(self,assumeZero):
zero = self.R.ideal(list(map(numerator, assumeZero)))
return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
def __add__(self,other):
"""Add two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top + self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)
return NotImplemented
def __sub__(self,other):
"""Subtract two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top - self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)
return NotImplemented
def __neg__(self):
"""Return the negation of a fraction."""
return fastfrac(self.R,-self.top,self.bot)
def __mul__(self,other):
"""Multiply two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.top,self.bot * other.bot)
return NotImplemented
def __rmul__(self,other):
"""Multiply something else with a fraction."""
return self.__mul__(other)
def __truediv__(self,other):
"""Divide two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top,self.bot * other)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
return NotImplemented
# Compatibility wrapper for Sage versions based on Python 2
def __div__(self,other):
"""Divide two fractions."""
return self.__truediv__(other)
def __pow__(self,other):
"""Compute a power of a fraction."""
if parent(other) == ZZ:
if other < 0:
# Negative powers require flipping top and bottom
return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))
else:
return fastfrac(self.R,self.top ^ other,self.bot ^ other)
return NotImplemented
def __str__(self):
return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"
def __repr__(self):
return "%s" % self
def numerator(self):
return self.top
class constraints:
"""A set of constraints, consisting of zero and nonzero expressions.
Constraints can either be used to express knowledge or a requirement.
Both the fields zero and nonzero are maps from expressions to description
strings. The expressions that are the keys in zero are required to be zero,
and the expressions that are the keys in nonzero are required to be nonzero.
Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in
nonzero could be multiplied into a single key. This is often much less
efficient to work with though, so we keep them separate inside the
constraints. This allows higher-level code to do fast checks on the individual
nonzero elements, or combine them if needed for stronger checks.
We can't multiply the different zero elements, as it would suffice for one of
the factors to be zero, instead of all of them. Instead, the zero elements are
typically combined into an ideal first.
"""
def __init__(self, **kwargs):
if 'zero' in kwargs:
self.zero = dict(kwargs['zero'])
else:
self.zero = dict()
if 'nonzero' in kwargs:
self.nonzero = dict(kwargs['nonzero'])
else:
self.nonzero = dict()
def negate(self):
return constraints(zero=self.nonzero, nonzero=self.zero)
def map(self, fun):
return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()})
def __add__(self, other):
zero = self.zero.copy()
zero.update(other.zero)
nonzero = self.nonzero.copy()
nonzero.update(other.nonzero)
return constraints(zero=zero, nonzero=nonzero)
def __str__(self):
return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)
def __repr__(self):
return "%s" % self
def normalize_factor(p):
"""Normalizes the sign of primitive polynomials (as returned by factor())
This function ensures that the polynomial has a positive leading coefficient.
This is necessary because recent sage versions (starting with v9.3 or v9.4,
we don't know) are inconsistent about the placement of the minus sign in
polynomial factorizations:
```
sage: R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
sage: R((-2 * (bx - ax)) ^ 1).factor()
(-2) * (bx - ax)
sage: R((-2 * (bx - ax)) ^ 2).factor()
(4) * (-bx + ax)^2
sage: R((-2 * (bx - ax)) ^ 3).factor()
(8) * (-bx + ax)^3
```
"""
# Assert p is not 0 and that its non-zero coeffients are coprime.
# (We could just work with the primitive part p/p.content() but we want to be
# aware if factor() does not return a primitive part in future sage versions.)
assert p.content() == 1
# Ensure that the first non-zero coefficient is positive.
return p if p.lc() > 0 else -p
def conflicts(R, con):
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
zero = R.ideal(list(map(numerator, con.zero)))
if 1 in zero:
return True
# First a cheap check whether any of the individual nonzero terms conflict on
# their own.
for nonzero in con.nonzero:
if nonzero.iszero(zero):
return True
# It can be the case that entries in the nonzero set do not individually
# conflict with the zero set, but their combination does. For example, knowing
# that either x or y is zero is equivalent to having x*y in the zero set.
# Having x or y individually in the nonzero set is not a conflict, but both
# simultaneously is, so that is the right thing to check for.
if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):
return True
return False
def get_nonzero_set(R, assume):
"""Calculate a simple set of nonzero expressions"""
zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = set()
for nz in map(numerator, assume.nonzero):
for (f,n) in nz.factor():
nonzero.add(normalize_factor(f))
rnz = zero.reduce(nz)
for (f,n) in rnz.factor():
nonzero.add(normalize_factor(f))
return nonzero
def prove_nonzero(R, exprs, assume):
"""Check whether an expression is provably nonzero, given assumptions"""
zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = get_nonzero_set(R, assume)
expl = set()
ok = True
for expr in exprs:
if numerator(expr) in zero:
return (False, [exprs[expr]])
allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
for (f, n) in allexprs.factor():
if normalize_factor(f) not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for (f, n) in zero.reduce(allexprs).factor():
if normalize_factor(f) not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in numerator(expr).factor():
if normalize_factor(f) not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in zero.reduce(numerator(expr)).factor():
if normalize_factor(f) not in nonzero:
expl.add(exprs[expr])
if expl:
return (False, list(expl))
else:
return (True, None)
def prove_zero(R, exprs, assume):
"""Check whether all of the passed expressions are provably zero, given assumptions"""
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
if not r:
return (False, list(map(lambda x: "Possibly zero denominator: %s" % x, e)))
zero = R.ideal(list(map(numerator, assume.zero)))
nonzero = prod(x for x in assume.nonzero)
expl = []
for expr in exprs:
if not expr.iszero(zero):
expl.append(exprs[expr])
if not expl:
return (True, None)
return (False, expl)
def describe_extra(R, assume, assumeExtra):
"""Describe what assumptions are added, given existing assumptions"""
zerox = assume.zero.copy()
zerox.update(assumeExtra.zero)
zero = R.ideal(list(map(numerator, assume.zero)))
zeroextra = R.ideal(list(map(numerator, zerox)))
nonzero = get_nonzero_set(R, assume)
ret = set()
# Iterate over the extra zero expressions
for base in assumeExtra.zero:
if base not in zero:
add = []
for (f, n) in numerator(base).factor():
if normalize_factor(f) not in nonzero:
add += ["%s" % normalize_factor(f)]
if add:
ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
# Iterate over the extra nonzero expressions
for nz in assumeExtra.nonzero:
nzr = zeroextra.reduce(numerator(nz))
if nzr not in zeroextra:
for (f,n) in nzr.factor():
if normalize_factor(zeroextra.reduce(f)) not in nonzero:
ret.add("%s != 0" % normalize_factor(zeroextra.reduce(f)))
return ", ".join(x for x in ret)
def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
"""Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions"""
assume = assumeLaw + assumeAssert + assumeBranch
if conflicts(R, assume):
# This formula does not apply
return (True, None)
describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
if describe != "":
describe = " (assuming " + describe + ")"
ok, msg = prove_zero(R, require.zero, assume)
if not ok:
return (False, "FAIL, %s fails%s" % (str(msg), describe))
res, expl = prove_nonzero(R, require.nonzero, assume)
if not res:
return (False, "FAIL, %s fails%s" % (str(expl), describe))
return (True, "OK%s" % describe)
def concrete_verify(c):
for k in c.zero:
if k != 0:
return (False, c.zero[k])
for k in c.nonzero:
if k == 0:
return (False, c.nonzero[k])
return (True, None)

Some files were not shown because too many files have changed in this diff Show more