diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28ef1685..690a95ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -713,8 +713,16 @@ jobs: cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE' - job_name: 'x86 (MSVC): Windows (VS 2022)' cmake_options: '-A Win32' - - job_name: 'x64 (MSVC): Windows (clang-cl)' - cmake_options: '-T ClangCL' + - job_name: 'x64 (clang-cl): Windows (VS 2022, shared)' + cmake_options: '-T ClangCL -DBUILD_SHARED_LIBS=ON' + symbol_check: 'true' + - job_name: 'x64 (clang-cl): Windows (VS 2022, static)' + cmake_options: '-T ClangCL -DBUILD_SHARED_LIBS=OFF' + - job_name: 'x64 (clang-cl): Windows (VS 2022, int128_struct)' + cmake_options: '-T ClangCL -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct' + - job_name: 'x64 (clang-cl): Windows (VS 2022, int128_struct with __(u)mulh)' + cmake_options: '-T ClangCL -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct' + cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE' steps: - name: Checkout diff --git a/CMakeLists.txt b/CMakeLists.txt index b3913c33..e9b4c8ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,10 +34,8 @@ set(CMAKE_C_EXTENSIONS OFF) #============================= # Configurable options #============================= -option(BUILD_SHARED_LIBS "Build shared libraries." ON) -option(SECP256K1_DISABLE_SHARED "Disable shared library. Overrides BUILD_SHARED_LIBS." OFF) -if(SECP256K1_DISABLE_SHARED) - set(BUILD_SHARED_LIBS OFF) +if(libsecp256k1_IS_TOP_LEVEL) + option(BUILD_SHARED_LIBS "Build shared libraries." ON) endif() option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL}) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 80890fb7..f0011086 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,12 +92,14 @@ Run the tests: To create a report, `gcovr` is recommended, as it includes branch coverage reporting: - $ gcovr --exclude 'src/bench*' --print-summary + $ gcovr --gcov-ignore-parse-errors=all --merge-mode-functions=separate --exclude 'src/bench*' --exclude 'src/modules/.*/bench_impl.h' --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 + $ gcovr --gcov-ignore-parse-errors=all --merge-mode-functions=separate --exclude 'src/bench*' --exclude 'src/modules/.*/bench_impl.h' --html --html-details -o coverage/coverage.html + +On `gcovr` >=8.3, `--gcov-ignore-parse-errors=all` can be replaced with `--gcov-suspicious-hits-threshold=140737488355330`. #### Exhaustive tests diff --git a/README.md b/README.md index 5baa4029..f786bbe5 100644 --- a/README.md +++ b/README.md @@ -91,13 +91,11 @@ To cross compile for Android with [NDK](https://developer.android.com/ndk/guides ### 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+. +The following example assumes Visual Studio 2022. Using clang-cl is recommended. In "Developer Command Prompt for VS 2022": - >cmake -G "Visual Studio 17 2022" -A x64 -B build + >cmake -B build -T ClangCL >cmake --build build --config RelWithDebInfo Usage examples diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index d1c3d5fa..1093288e 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -19,7 +19,7 @@ RUN dpkg --add-architecture i386 && \ dpkg --add-architecture arm64 && \ dpkg --add-architecture ppc64el -# dkpg-dev: to make pkg-config work in cross-builds +# dpkg-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 \ diff --git a/configure.ac b/configure.ac index f07e6a98..be77c847 100644 --- a/configure.ac +++ b/configure.ac @@ -144,7 +144,7 @@ AC_ARG_ENABLE(benchmark, [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]]), [], + AS_HELP_STRING([--enable-coverage],[enable coverage analysis support [default=no]]), [], [SECP_SET_DEFAULT([enable_coverage], [no], [no])]) AC_ARG_ENABLE(tests, diff --git a/include/secp256k1_ellswift.h b/include/secp256k1_ellswift.h index 0d1293e9..4cda5d5c 100644 --- a/include/secp256k1_ellswift.h +++ b/include/secp256k1_ellswift.h @@ -130,7 +130,7 @@ SECP256K1_API int secp256k1_ellswift_decode( * * Returns: 1: secret was valid, public key was stored. * 0: secret was invalid, try again. - * Args: ctx: pointer to a context object + * Args: ctx: pointer to a context object (not secp256k1_context_static) * Out: ell64: pointer to a 64-byte array to receive the ElligatorSwift * public key * In: seckey32: pointer to a 32-byte secret key diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 070a1213..b943fe2a 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -213,7 +213,7 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 * but this would simply discard the bits that fall off at the bottom, * and thus, for example, bitdata could still have only two values if we * happen to shift by exactly 31 positions. We use a rotation instead, - * which ensures that bitdata doesn't loose entropy. This relies on the + * which ensures that bitdata doesn't lose entropy. This relies on the * rotation being atomic, i.e., the compiler emitting an actual rot * instruction. */ uint32_t bitdata = secp256k1_rotr32(recoded[bit_pos >> 5], bit_pos & 0x1f); @@ -242,7 +242,7 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, * by Dag Arne Osvik, Adi Shamir, and Eran Tromer - * (https://www.tau.ac.il/~tromer/papers/cache.pdf) + * (https://eprint.iacr.org/2005/271.pdf) */ for (index = 0; index < COMB_POINTS; ++index) { secp256k1_ge_storage_cmov(&adds, &secp256k1_ecmult_gen_prec_table[block][index], index == abs); diff --git a/src/hash_impl.h b/src/hash_impl.h index 956e0ea4..1065acd6 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -265,7 +265,7 @@ static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 while (outlen > 0) { secp256k1_hmac_sha256 hmac; - int now = outlen; + size_t now = outlen; secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); secp256k1_hmac_sha256_write(&hmac, rng->v, 32); secp256k1_hmac_sha256_finalize(&hmac, rng->v); diff --git a/src/modules/ellswift/tests_impl.h b/src/modules/ellswift/tests_impl.h index 3c314c9b..b90fd0ab 100644 --- a/src/modules/ellswift/tests_impl.h +++ b/src/modules/ellswift/tests_impl.h @@ -405,31 +405,31 @@ void run_ellswift_tests(void) { /* Test hash initializers. */ { - secp256k1_sha256 sha, sha_optimized; + secp256k1_sha256 sha_optimized; + /* "secp256k1_ellswift_encode" */ static const unsigned char encode_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'e', 'n', 'c', 'o', 'd', 'e'}; + /* "secp256k1_ellswift_create" */ static const unsigned char create_tag[] = {'s', 'e', 'c', 'p', '2', '5', '6', 'k', '1', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'c', 'r', 'e', 'a', 't', 'e'}; + /* "bip324_ellswift_xonly_ecdh" */ static const unsigned char bip324_tag[] = {'b', 'i', 'p', '3', '2', '4', '_', 'e', 'l', 'l', 's', 'w', 'i', 'f', 't', '_', 'x', 'o', 'n', 'l', 'y', '_', 'e', 'c', 'd', 'h'}; /* Check that hash initialized by * secp256k1_ellswift_sha256_init_encode has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, encode_tag, sizeof(encode_tag)); secp256k1_ellswift_sha256_init_encode(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, encode_tag, sizeof(encode_tag)); /* Check that hash initialized by * secp256k1_ellswift_sha256_init_create has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, create_tag, sizeof(create_tag)); secp256k1_ellswift_sha256_init_create(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, create_tag, sizeof(create_tag)); /* Check that hash initialized by * secp256k1_ellswift_sha256_init_bip324 has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, bip324_tag, sizeof(bip324_tag)); secp256k1_ellswift_sha256_init_bip324(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, bip324_tag, sizeof(bip324_tag)); } } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 38f4a80d..a9a4f275 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -696,40 +696,39 @@ static void scriptless_atomic_swap(void) { CHECK(secp256k1_schnorrsig_verify(CTX, final_sig_a, msg32_a, sizeof(msg32_a), &agg_pk_a) == 1); } -static void sha256_tag_test_internal(secp256k1_sha256 *sha_tagged, unsigned char *tag, size_t taglen) { - secp256k1_sha256 sha; - secp256k1_sha256_initialize_tagged(&sha, tag, taglen); - test_sha256_eq(&sha, sha_tagged); -} - /* Checks that the initialized tagged hashes have the expected * state. */ static void sha256_tag_test(void) { secp256k1_sha256 sha; { - char tag[] = "KeyAgg list"; + /* "KeyAgg list" */ + static const unsigned char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'l', 'i', 's', 't'}; secp256k1_musig_keyagglist_sha256(&sha); - sha256_tag_test_internal(&sha, (unsigned char*)tag, sizeof(tag) - 1); + test_sha256_tag_midstate(&sha, tag, sizeof(tag)); } { - char tag[] = "KeyAgg coefficient"; + /* "KeyAgg coefficient" */ + static const unsigned char tag[] = {'K', 'e', 'y', 'A', 'g', 'g', ' ', 'c', 'o', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 't'}; secp256k1_musig_keyaggcoef_sha256(&sha); - sha256_tag_test_internal(&sha, (unsigned char*)tag, sizeof(tag) - 1); + test_sha256_tag_midstate(&sha, tag, sizeof(tag)); } { - unsigned char tag[] = "MuSig/aux"; + /* "MuSig/aux" */ + static const unsigned char tag[] = { 'M', 'u', 'S', 'i', 'g', '/', 'a', 'u', 'x' }; secp256k1_nonce_function_musig_sha256_tagged_aux(&sha); - sha256_tag_test_internal(&sha, (unsigned char*)tag, sizeof(tag) - 1); + test_sha256_tag_midstate(&sha, tag, sizeof(tag)); } { - unsigned char tag[] = "MuSig/nonce"; + /* "MuSig/nonce" */ + static const unsigned char tag[] = { 'M', 'u', 'S', 'i', 'g', '/', 'n', 'o', 'n', 'c', 'e' }; secp256k1_nonce_function_musig_sha256_tagged(&sha); - sha256_tag_test_internal(&sha, (unsigned char*)tag, sizeof(tag) - 1); + test_sha256_tag_midstate(&sha, tag, sizeof(tag)); } { - unsigned char tag[] = "MuSig/noncecoef"; + /* "MuSig/noncecoef" */ + static const unsigned char tag[] = { 'M', 'u', 'S', 'i', 'g', '/', 'n', 'o', 'n', 'c', 'e', 'c', 'o', 'e', 'f' }; secp256k1_musig_compute_noncehash_sha256_tagged(&sha); - sha256_tag_test_internal(&sha, (unsigned char*)tag, sizeof(tag) - 1); + test_sha256_tag_midstate(&sha, tag, sizeof(tag)); } } diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 2ed7be67..13d92449 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -139,7 +139,7 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_gej rj; secp256k1_ge pk; secp256k1_ge r; - unsigned char buf[32] = { 0 }; + unsigned char nonce32[32] = { 0 }; unsigned char pk_buf[32]; unsigned char seckey[32]; int ret = 1; @@ -164,8 +164,8 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_scalar_get_b32(seckey, &sk); secp256k1_fe_get_b32(pk_buf, &pk.x); - ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata); - secp256k1_scalar_set_b32(&k, buf, NULL); + ret &= !!noncefp(nonce32, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata); + secp256k1_scalar_set_b32(&k, nonce32, NULL); ret &= !secp256k1_scalar_is_zero(&k); secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); @@ -191,6 +191,7 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_scalar_clear(&k); secp256k1_scalar_clear(&sk); secp256k1_memclear(seckey, sizeof(seckey)); + secp256k1_memclear(nonce32, sizeof(nonce32)); secp256k1_gej_clear(&rj); return ret; diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 2d716a01..5abbeefe 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -21,11 +21,12 @@ static void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, s } static void run_nonce_function_bip340_tests(void) { - unsigned char tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; - unsigned char aux_tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'a', 'u', 'x'}; + /* "BIP0340/nonce" */ + static const unsigned char tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; + /* "BIP0340/aux" */ + static const unsigned char aux_tag[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'a', 'u', 'x'}; unsigned char algo[] = {'B', 'I', 'P', '0', '3', '4', '0', '/', 'n', 'o', 'n', 'c', 'e'}; size_t algolen = sizeof(algo); - secp256k1_sha256 sha; secp256k1_sha256 sha_optimized; unsigned char nonce[32], nonce_z[32]; unsigned char msg[32]; @@ -39,16 +40,15 @@ static void run_nonce_function_bip340_tests(void) { /* Check that hash initialized by * secp256k1_nonce_function_bip340_sha256_tagged has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, tag, sizeof(tag)); + /* Check that hash initialized by * secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected * state. */ - secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized); - test_sha256_eq(&sha, &sha_optimized); + test_sha256_tag_midstate(&sha_optimized, aux_tag, sizeof(aux_tag)); testrand256(msg); testrand256(key); diff --git a/src/tests.c b/src/tests.c index 5e05030e..1c7ee422 100644 --- a/src/tests.c +++ b/src/tests.c @@ -655,6 +655,13 @@ static void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 CHECK(sha1->bytes == sha2->bytes); CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); } +/* Convenience function for using test_sha256_eq to verify the correctness of a + * tagged hash midstate. This function is used by some module tests. */ +static void test_sha256_tag_midstate(secp256k1_sha256 *sha_tagged, const unsigned char *tag, size_t taglen) { + secp256k1_sha256 sha; + secp256k1_sha256_initialize_tagged(&sha, tag, taglen); + test_sha256_eq(&sha, sha_tagged); +} static void run_hmac_sha256_tests(void) { static const char *keys[6] = { @@ -3960,7 +3967,7 @@ static void test_ge(void) { free(gej); } -static void test_intialized_inf(void) { +static void test_initialized_inf(void) { secp256k1_ge p; secp256k1_gej pj, npj, infj1, infj2, infj3; secp256k1_fe zinv; @@ -4086,7 +4093,7 @@ static void run_ge(void) { test_ge(); } test_add_neg_y_diff_x(); - test_intialized_inf(); + test_initialized_inf(); test_ge_bytes(); } diff --git a/src/wycheproof/WYCHEPROOF_COPYING b/src/wycheproof/WYCHEPROOF_COPYING index 26957043..c9a4ef81 100644 --- a/src/wycheproof/WYCHEPROOF_COPYING +++ b/src/wycheproof/WYCHEPROOF_COPYING @@ -1,7 +1,7 @@ * The file `ecdsa_secp256k1_sha256_bitcoin_test.json` in this directory comes from project Wycheproof with git commit - `df4e933efef449fc88af0c06e028d425d84a9495`, see - https://github.com/C2SP/wycheproof/blob/df4e933efef449fc88af0c06e028d425d84a9495/testvectors_v1/ecdsa_secp256k1_sha256_bitcoin_test.json + `7ae4532f417575ced2b1cbbabed81a7fecfaef5d`, see + https://github.com/C2SP/wycheproof/blob/7ae4532f417575ced2b1cbbabed81a7fecfaef5d/testvectors_v1/ecdsa_secp256k1_sha256_bitcoin_test.json * The file `ecdh_secp256k1_test.json` in this directory comes from project Wycheproof with git commit diff --git a/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json b/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json index aa0cc8a4..add468f5 100644 --- a/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json +++ b/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json @@ -5,7 +5,7 @@ "numberOfTests" : 463, "header" : [ "Test vectors of type EcdsaBitcoinVerify are meant for the verification", - "of a ECDSA variant used for bitcoin, that add signature non-malleability." + "of a ECDSA variant used for Bitcoin, that add signature non-malleability." ], "notes" : { "ArithmeticError" : { @@ -47,7 +47,7 @@ "InvalidSignature" : { "bugType" : "AUTH_BYPASS", "description" : "The signature contains special case values such as r=0 and s=0. Buggy implementations may accept such values, if the implementation does not check boundaries and computes s^(-1) == 0.", - "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowing the message to sign.", "cves" : [ "CVE-2022-21449", "CVE-2021-43572", @@ -57,7 +57,7 @@ "InvalidTypesInSignature" : { "bugType" : "AUTH_BYPASS", "description" : "The signature contains invalid types. Dynamic typed languages sometime coerce such values of different types into integers. If an implementation is careless and has additional bugs, such as not checking integer boundaries then it may be possible that such signatures are accepted.", - "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowing the message to sign.", "cves" : [ "CVE-2022-21449" ] @@ -95,8 +95,8 @@ }, "SignatureMalleabilityBitcoin" : { "bugType" : "SIGNATURE_MALLEABILITY", - "description" : "\"BitCoins\"-curves are curves where signature malleability can be a serious issue. An implementation should only accept a signature s where s < n/2. If an implementation is not meant for uses cases that require signature malleability then this implementation should be tested with another set of test vectors.", - "effect" : "In bitcoin exchanges, it may be used to make a double deposits or double withdrawals", + "description" : "Signature malleability can be a serious issue in Bitcoin. An implementation should only accept a signature s where s < n/2. If an implementation is meant for use cases that tolerate signature malleability then this implementation should not be tested with this set of test vectors.", + "effect" : "In Bitcoin exchanges, it may be used to make a double deposits or double withdrawals", "links" : [ "https://en.bitcoin.it/wiki/Transaction_malleability", "https://en.bitcoinwiki.org/wiki/Transaction_Malleability" diff --git a/tools/check-abi.sh b/tools/check-abi.sh index 601a64ba..a3ca67a6 100755 --- a/tools/check-abi.sh +++ b/tools/check-abi.sh @@ -49,14 +49,7 @@ checkout_and_build() { -DSECP256K1_BUILD_CTIME_TESTS=OFF \ -DSECP256K1_BUILD_EXAMPLES=OFF cmake --build . -j "$(nproc)" - # FIXME: Just set LIBPATH to lib/libsecp256k1.so once version 0.6.0 is - # released. - if [ -f "src/libsecp256k1.so" ]; then - LIBPATH="src/libsecp256k1.so" - else - LIBPATH="lib/libsecp256k1.so" - fi - abi-dumper $LIBPATH -o ABI.dump -lver "$2" -public-headers ../include/ + abi-dumper lib/libsecp256k1.so -o ABI.dump -lver "$2" -public-headers ../include/ cd "$_orig_dir" }