mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Squashed 'src/secp256k1/' changes from 43dd1f4fe7..0129b77767
0129b77767 Merge #113: Upstream PRs #849 #851 e1756dfddc Merge commits '3a106966 8f0c6f15 ' into temp-merge-851 7093e633b8 Merge pull request #106 from apoelstra/2020-11-reduce-test-rounds 29f9a7dc62 reduce test rounds for rangeproof and surjectionproof 8f0c6f1545 Merge #851: make test count iteration configurable by environment variable f4fa8d226a forbid a test iteration of 0 or less ff4714e641 Merge pull request #105 from jonasnick/update-musig 3fb4d6db9c travis: run musig test whenever schnorrsig tests are run b9d91b3ecb musig: add pubkey_tweak_add function to allow taproot tweaking 0d71b6c61f Merge pull request #112 from jgriffiths/missed_rename 4721bec0ef Update renamed decl missed in e0ced690cff035b61763686cb69b7d06571e23e2 ebf57dc2f5 Merge pull request #107 from thomaseizinger/secp256k1-zkp 4d20713425 Remove unused context initializer functions 3a106966aa Merge #849: Convert Sage code to Python 3 (as used by Sage >= 9) 13c88efed0 Convert Sage code to Python 3 (as used by Sage >= 9) 0ce4554881 make test count iteration configurable by environment variable 38a8b20991 musig: fix memory leak in musig test 5b4eb18ec5 musig: shorten partial nonce byte array from 33 to 32 bytes 62f0b2d867 musig: make musig partial nonces byte arrays instead of "pubkeys" 73792e4a27 musig: represent a combined_nonce as an xonly_pubkey 2117e7466a musig: improve variable naming and be consistent with schnorrsig module ebc31f1f9d musig: add ARG_CHECKs to functions to help debuggability ac2d0e6697 musig: add magic to session to detect if session is uninitalized 29b4bd85d7 musig: simplify state machine by adding explicit round to session struct 6370bdd537 Merge pull request #104 from jonasnick/temp-merge-835 e0ced690cf Rename rands64 to testrandi64 b0917f3de1 Merge remote-tracking branch 'upstream/master' into temp-merge-835 81052ca411 Merge #103: Merge upstream schnorrsig PR 9e5939d284 Merge #835: Don't use reserved identifiers memczero and benchmark_verify_t 96b9236c42 re-enable musig module 23900a0d86 Fix the MuSig module after integrating bip-schnorr updates 005fe79262 Merge commit '8ab24e8d' into tmp a11250330b (actually) remove schnorrsig module bac746c55e (temporarily) disable musig module d0a83f7328 Merge #839: Prevent arithmetic on NULL pointer if the scratch space is too small 903b16aa6c Merge #840: Return NULL early in context_preallocated_create if flags invalid 1f4dd03838 Typedef (u)int128_t only when they're not provided by the compiler 3967d96bf1 Merge #838: Make autotools check for all the used openssl functions 3734b68200 Configure echo if openssl tests are enabled ebfa2058e9 Return NULL early in context_preallocated_create if flags invalid 6f54e69f03 Merge #841: Avoids a potentially shortening size_t to int cast in strauss_wnaf_ 29a299e373 Run the undefined behaviour sanitizer on Travis 7506e064d7 Prevent arithmetic on NULL pointer if the scratch space is too small 8893f42438 Avoids a potentially shortening size_t to int cast in strauss_wnaf_ e6692778d3 Modify bitcoin_secp.m4's openssl check to call all the functions that we use in the tests/benchmarks. That way linking will fail if those symbols are missing ac05f61fcf Merge #809: Stop treating ECDH as experimental e6e3d5da2f travis: add schnorrsig to valgrind and big endian platform test 353dff156f Stop treating ECDH as experimental e89278f211 Don't use reserved identifiers memczero and benchmark_verify_t c6b6b8f1bb Merge #830: Rip out non-endomorphism code + dependencies c582abade1 Consistency improvements to the comments 63c6b71616 Reorder comments/function around scalar_split_lambda 2edc514c90 WNAF of lambda_split output has max size 129 4232e5b7da Rip out non-endomorphism code ebad8414b0 Check correctness of lambda split without -DVERIFY fe7fc1fda8 Make lambda constant accessible 9d2f2b44d8 Add tests to exercise lambda split near bounds 9aca2f7f07 Add secp256k1_split_lambda_verify acab934d24 Detailed comments for secp256k1_scalar_split_lambda 73acc8fef6 Merge pull request #102 from jonasnick/temp-merge-797 8b70795b5e Fix BE platforms by updating endianness macros to match upstream 76ed922a5f Increase precision of g1 and g2 6173839c90 Switch to our own memcmp function d1b13b0014 Merge commit 'f3733c54' into temp-merge-797 23bf5b732b Merge pull request #101 from jonasnick/temp-merge-778 0a5b60d8b0 Merge commit '6034a04f' into temp-merge-778 caa5d24446 Merge #99: [upstream PR #774]: tests: Abort if malloc() fails during context cloning tests 1789183cba Merge commit '40412b19' into temp-merge-774 63150ab4da Merge #827: Rename testrand functions to have test in name c5257aed0b Merge #821: travis: Explicitly set --with-valgrind bb1f54280f Merge #818: Add static assertion that uint32_t is unsigned int or wider a45c1fa63c Rename testrand functions to have test in name 5006895bd6 Merge #808: Exhaustive test improvements + exhaustive schnorrsig tests a39b08d672 Merge #95: [upstream PR #741]: Remove unnecessary sign variable from wnaf_const a3a3a17f47 Merge pull request #94 from apoelstra/temp-merge-1309c03c45beece646a7d21fdb6a0e3d38adee2b 4eecb4d6ef travis: VALGRIND->RUN_VALGRIND to avoid confusion with WITH_VALGRIND 66a765c775 travis: Explicitly set --with-valgrind d7838ba6a6 Merge #813: Enable configuring Valgrind support 7ceb0b7611 Merge #819: Enable -Wundef warning 8b7dcdd955 Add exhaustive test for extrakeys and schnorrsig 08d7d89299 Make pubkey parsing test whether points are in the correct subgroup 87af00b511 Abstract out challenge computation in schnorrsig 63e1b2aa7d Disable output buffering in tests_exhaustive.c 39f67dd072 Support splitting exhaustive tests across cores e99b26fcd5 Give exhaustive_tests count and seed cmdline inputs 49e6630bca refactor: move RNG seeding to testrand b110c106fa Change exhaustive test groups so they have a point with X=1 cec7b18a34 Select exhaustive lambda in function of order 78f6cdfaae Make the curve B constant a secp256k1_fe d7f39ae4b6 Delete gej_is_valid_var: unused outside tests 8bcd78cd79 Make secp256k1_scalar_b32 detect overflow in scalar_low c498366e5b Move exhaustive tests for recovery to module be31791543 Make group order purely compile-time in exhaustive tests e73ff30922 Enable -Wundef warning c0041b5cfc Add static assertion that uint32_t is unsigned int or wider 4ad408faf3 Merge #782: Check if variable=yes instead of if var is set in travis.sh 412bf874d0 configure: Allow specifying --with[out]-valgrind explicitly 34debf7a6d Modify .travis.yml to explictly pass no in env vars instead of setting to nothing a0e99fc121 Merge #814: tests: Initialize random group elements fully 5738e8622d tests: Initialize random group elements fully c9939ba55d Merge #812: travis: run bench_schnorrsig a51f2af62b travis: run bench_schnorrsig 8ab24e8dad Merge #558: Add schnorrsig module which implements BIP-340 compliant signatures f3733c5433 Merge #797: Fix Jacobi benchmarks and other benchmark improvements cb5524adc5 Add benchmark for secp256k1_ge_set_gej_var 5c6af60ec5 Make jacobi benchmarks vary inputs d0fdd5f009 Randomize the Z coordinates in bench_internal c7a3424c5f Rename bench_internal variables 875d68b95f Merge #699: Initialize field elements when resulting in infinity 54caf2e74f Merge #799: Add fallback LE/BE for architectures with known endianness + SHA256 selftest f431b3f28a valgrind_ctime_test: Add schnorrsig_sign 16ffa9d97c schnorrsig: Add taproot test case 8dfd53ee3f schnorrsig: Add benchmark for sign and verify 4e43520026 schnorrsig: Add BIP-340 compatible signing and verification 7332d2db6b schnorrsig: Add BIP-340 nonce function 7a703fd97d schnorrsig: Init empty experimental module eabd9bc46a Allow initializing tagged sha256 6fcb5b845d extrakeys: Add keypair_xonly_tweak_add 58254463f9 extrakeys: Add keypair struct with create, pub and pub_xonly f0010349b8 Separate helper functions for pubkey_create and seckey_tweak_add 910d9c284c extrakeys: Add xonly_pubkey_tweak_add & xonly_pubkey_tweak_add_test 176bfb1110 Separate helper function for ec_pubkey_tweak_add 4cd2ee474d extrakeys: Add xonly_pubkey with serialize, parse and from_pubkey f49c9896b0 Merge #806: Trivial: Add test logs to gitignore aabf00c155 Merge #648: Prevent ints from wrapping around in scratch space functions f5adab16a9 Merge #805: Remove the extremely outdated TODO file. bceefd6547 Add test logs to gitignore 1c325199d5 Remove the extremely outdated TODO file. 47e6618e11 extrakeys: Init empty experimental module 3e08b02e2a Make the secp256k1_declassify argument constant 8bc6aeffa9 Add SHA256 selftest 670cdd3f8b Merge #798: Check assumptions on integer implementation at compile time 5e5fb28b4a Use additional system macros to figure out endianness 7c068998ba Compile-time check assumptions on integer types 02b6c87b52 Add support for (signed) __int128 979961c506 Merge #787: Use preprocessor macros instead of autoconf to detect endianness 887bd1f8b6 Merge #793: Make scalar/field choice depend on C-detected __int128 availability 0dccf98a21 Use preprocessor macros instead of autoconf to detect endianness b2c8c42cf1 Merge #795: Avoid linking libcrypto in the valgrind ct test. 57d3a3c64c Avoid linking libcrypto in the valgrind ct test. 79f1f7a4f1 Autodetect __int128 availability on the C side 0d7727f95e Add SECP256K1_FE_STORAGE_CONST_GET to 5x52 field 805082de11 Merge #696: Run a Travis test on s390x (big endian) 39295362cf Test travis s390x (big endian) ef37761fee Change travis.sh to check if variables are equal to yes instead of not-empty. Before this, setting `VALGRIND=wat` was considered as true, and to make it evaluate as false you had to unset the variable `VALGRIND=` but not it checks if `VALGRIND=yes` and if it's not `yes` then it's evaluated to false 6034a04fb1 Merge #778: secp256k1_gej_double_nonzero supports infinity f60915906d Merge #779: travis: Fix argument quoting for ./configure 9e49a9b255 travis: Fix argument quoting for ./configure 18d36327fd secp256k1_gej_double_nonzero supports infinity 214cb3c321 Merge #772: Improve constant-timeness on PowerPC 40412b1930 Merge #774: tests: Abort if malloc() fails during context cloning tests 2e1b9e0458 tests: Abort if malloc() fails during context cloning tests 67a429f31f Suppress a harmless variable-time optimization by clang in _int_cmov 5b196338f0 Remove redundant "? 1 : 0" after comparisons in scalar code 3e5cfc5c73 Merge #741: Remove unnecessary sign variable from wnaf_const 66bb9320c0 Merge #773: Fix some compile problems on weird/old compilers. 1309c03c45 Fix some compile problems on weird/old compilers. fabc8f74e7 Fix typo in MuSig documentation. 96201b4f6e Require message in musig protocol in an earlier state. In particular, remove the set_msg function and require the message in get_public_nonce at the latest. 4fd0d56e37 Fix my_index in musig state machine tests b74f2dc478 Remove mentions of DER in H derivation. b368a5d163 Fix ARG_NONNULL macro usage in musig include bedff79848 Add cplusplus directive to musig include 9957307c3f Fix explanation of H derivation. It doesn't use DER encoding. d924027765 Add tweak32 parameter to musig_partial_sig_combine which allows to sign for p2c/taproot commitments a4410ac779 Add musig module tests to travis d6738e890e surjection proof: Reject proofs with too many used inputs in reduced mode bd70820123 allow reducing surjection proof size (to lower generation stack usage) 56f69d979f surjectionproof: introduce `SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS` constant and set it to 16 b8a3ff5f3b surjectionproof: reduce stack usage 68d937fe11 surjectionproof: fix malleability in surjection proof parsing 41bc9ce129 surjectionproof: add test vectors for "set padding bits" b0644d4ab3 surjectionproof: add fixed test vectors c0415eb0cb Fix read of wrong buffer (and OOB) in surjectionproof tests 00fffeb172 Improve comments for surctionproof init+alloc/destroy funcs 2dc868f35b work in progress: add _allocate_initialized/destroy funcs 0d4ee3c62d Improve explanation of key cancellation attack in whitelist.md 2a1750dedd Clarify how to derive alternative generator H ed7394f005 Add bench_generator and bench_rangeproof to .gitignore 9dd117fd2b Clean up ./configure help strings (zkp extensions) f35b5e271f Fix a small typo in the generator parameter name 068f03c35b generator: remove `CHECK` abort calls exposed by public API 3424cb1fa3 musig: add user documentation 13ef445721 Add 3-of-3 MuSig example b86c210747 Add MuSig module which allows creating n-of-n multisignatures and adaptor signatures. c59c602dd6 Add schnorrsig module which implements BIP-schnorr [0] compatible signing, verification and batch verification. a1f16a0a53 add chacha20 function 3cdc02ef8a use proper types for rangeproof min/max cf21c9d715 rangeproof: reduce iteration count in unit tests 0dfb356f95 Enable more builds with rest of experimental flags 4c231568fb Add explanation about how BIP32 unhardened derivation can be used to simplify whitelisting f416e039bb Add comment to explain effect of max_n_iterations in surjectionproof_init 936d62f248 add unit test for generator and pedersen commitment roundtripping e06540de8c rangeproof: fix serialization of pedersen commintments edb879f578 rangeproof: verify correctness of pedersen commitments when parsing fca4c3b62f generator: verify correctness of point when parsing c50b218698 rangeproof: check that points deserialize correctly when verifying rangeproof c33e597245 rangeproof: add fixed vector test case 0c5cb7cd08 Expose generator in shared library dbc49df80c fix spelling in documentation 47be098bac Test for rejection of trailing bytes in range proofs 16aaa4a02c Test for rejection of trailing bytes in surjection proofs 949e994cb3 Reject surjection proofs with trailing garbage c87618157e Minor bugfix. Wrong length due to NUL character. fc3dc94049 Add whitelisting benchmark edc7cb6cdd add whitelist_impl.h to include for dist 4320490e88 generator: add API tests 126493ef01 generator: remove unnecessary ARG_CHECK from generate() 253f131310 Fix generator makefile 3997128ad9 Fix pedersen_blind_generator_blind_sum return value documentation 04f4c09111 Add n_keys argument to whitelist_verify dbf3d752a8 Fix checks of whitelist serialize/parse arguments 29d0d562dc whitelist: fix serialize/parse API to take serialized length 660ad39fb3 Fix include/secp256k1_rangeproof.h function argument documentation. e13bdf2f23 rangeproof: add API tests 18c5c62b45 surjectionproof: rename unit test functions to be more consistent with other modules 5f1ad03d00 surjectionproof: add API unit tests f858a4e3d5 surjectionproof: tests_impl.h s/assert/CHECK/g 002002e735 rangeproof: fix memory leak in unit tests ba8b4f53ef add surjection proof module 8c77fe1590 Implement ring-signature based whitelist delegation scheme 94425d4a67 rangeproof: several API changes f6c84a02f3 Expose generator in pedersen/rangeproof API 360e218043 Constant-time generator module e7a8a5f638 rangeproof: expose sidechannel message field in the signing API a88db4a744 [RANGEPROOF BREAK] Use quadratic residue for tie break and modularity cleanup 16618fcd8d Pedersen commitments, borromean ring signatures, and ZK range proofs. 3cf8f70ba1 Add 64-bit integer utilities 2309c7dd4a Merge #769: Undef HAVE___INT128 in basic-config.h to fix gen_context compilation 22e578bb11 Undef HAVE___INT128 in basic-config.h to fix gen_context compilation 3f4a5a10e4 Merge #765: remove dead store in ecdsa_signature_parse_der_lax f00d6575ca remove dead store in ecdsa_signature_parse_der_lax dbd41db16a Merge #759: Fix uninitialized variables in ecmult_multi test 2e7fc5b537 Fix uninitialized variables in ecmult_multi test 2ed54da18a Merge #755: Recovery signing: add to constant time test, and eliminate non ct operators 28609507e7 Add tests for the cmov implementations 73596a85a2 Add ecdsa_sign_recoverable to the ctime tests 2876af4f8d Split ecdsa_sign logic into a new function and use it from ecdsa_sign and recovery 5e1c885efb Merge #754: Fix uninit values passed into cmov f79a7adcf5 Add valgrind uninit check to cmovs output 05d315affe Merge #752: autoconf: Use ":" instead of "dnl" as a noop a39c2b09de Fixed UB(arithmetics on uninit values) in cmovs 3a6fd7f636 Merge #750: Add macOS to the CI 5e8747ae2a autoconf: Use ":" instead of "dnl" as a noop 71757da5cc Explictly pass SECP256K1_BENCH_ITERS to the benchmarks in travis.sh 99bd661d71 Replace travis_wait with a loop printing "\a" to stdout every minute bc818b160c Bump travis Ubuntu from xenial(16.04) to bionic(18.04) 0c5ff9066e Add macOS support to travis b6807d91d8 Move travis script into a standalone sh file f39f99be0e Merge #701: Make ec_ arithmetic more consistent and add documentation 37dba329c6 Remove unnecessary sign variable from wnaf_const 6bb0b77e15 Fix test_constant_wnaf for -1 and add a test for it. 39198a03ea Merge #732: Retry if r is zero during signing 59a8de8f64 Merge #742: Fix typo in ecmult_const_impl.h 4e284655d9 Fix typo in ecmult_const_impl.h f862b4ca13 Merge #740: Make recovery/main_impl.h non-executable ffef45c98a Make recovery/main_impl.h non-executable 2361b3719a Merge #735: build: fix OpenSSL EC detection on macOS 3b7d26b23c build: add SECP_TEST_INCLUDES to bench_verify CPPFLAGS 84b5fc5bc3 build: fix OpenSSL EC detection on macOS 37ed51a7ea Make ecdsa_sig_sign constant-time again after reverting 25e3cfb 93d343bfc5 Revert "ecdsa_impl: replace scalar if-checks with VERIFY_CHECKs in ecdsa_sig_sign" 7e3952ae82 Clarify documentation of tweak functions. 89853a0f2e Make tweak function documentation more consistent. 41fc785602 Make ec_privkey functions aliases for ec_seckey_negate, ec_seckey_tweak_add and ec_seckey_mul 22911ee6da Rename private key to secret key in public API (with the exception of function names) 5a73f14d6c Mention that value is unspecified for In/Out parameters if the function returns 0 f03df0e6d7 Define valid ECDSA keys in the documentation of seckey_verify 5894e1f1df Return 0 if the given seckey is invalid in privkey_negate, privkey_tweak_add and privkey_tweak_mul 8f814cddb9 Add test for boundary conditions of scalar_set_b32 with respect to overflows 3fec982608 Use scalar_set_b32_seckey in ecdsa_sign, pubkey_create and seckey_verify 9ab2cbe0eb Add scalar_set_b32_seckey which does the same as scalar_set_b32 and also returns whether it's a valid secret key 4f27e344c6 Merge #728: Suppress a harmless variable-time optimization by clang in memczero 01993878bb Add test for memczero() 52a03512c1 Suppress a harmless variable-time optimization by clang in memczero 8f78e208ad Merge #722: Context isn't freed in the ECDH benchmark ed1b91171a Merge #700: Allow overriding default flags 85b35afa76 Add running benchmarks regularly and under valgrind in travis ca4906b02e Pass num of iters to benchmarks as variable, and define envvar 02dd5f1bbb free the ctx at the end of bench_ecdh e9fccd4de1 Merge #708: Constant-time behaviour test using valgrind memtest. 08fb6c4926 Run valgrind_ctime_test in travis 3d2302257f Constant-time behaviour test using valgrind memtest. 96d8ccbd16 Merge #710: Eliminate harmless non-constant time operations on secret data. 0585b8b2ee Merge #718: Clarify that a secp256k1_ecdh_hash_function must return 0 or 1 7b50483ad7 Adds a declassify operation to aid constant-time analysis. 34a67c773b Eliminate harmless non-constant time operations on secret data. ca739cba23 Compile with optimization flag -O2 by default instead of -O3 eb45ef3384 Clarify that a secp256k1_ecdh_hash_function must return 0 or 1 856a01d6ad Merge #714: doc: document the length requirements of output parameter. d72b9e2483 Merge #682: Remove Java Native Interface 4b48a43106 doc: document the length requirements of output parameter. 1b4d256e2e Merge #713: Docstrings dabfea7e21 field: extend docstring of secp256k1_fe_normalize dc7d8fd9e2 scalar: extend docstring of secp256k1_scalar_set_b32 074ab582dd Merge #704: README: add a section for test coverage acb7f97eb8 README: add a section for test coverage 227a4f2d07 Merge #709: Remove secret-dependant non-constant time operation in ecmult_const. d567b779fe Clarify comments about use of rzr on ge functions and abs function. 2241ae6d14 Remove secret-dependant non-constant time operation in ecmult_const. 642cd062bd Remove Java Native Interface 83fb1bcef4 Remove -O2 from default CFLAGS because this would override the -O3 flag (see AC_PROG_CC in the Autoconf manual) ecba8138ec Append instead of Prepend user-CFLAGS to default CFLAGS allowing the user to override default variables 613c34cd86 Remove test in configure.ac because it doesn't have an effect f45d897101 Merge #703: Overhaul README.md 2e759ec753 Overhaul README.md d644dda5c9 Merge #689: Remove "except in benchmarks" exception for fp math bde2a32286 Convert bench.h to fixed-point math 47a7b8382f Clear field elements when writing infinity 61d1ecb028 Added test with additions resulting in infinity 387d723c3f Merge #679: Add SECURITY.md 0db61d25c9 Merge #685: Fix issue where travis does not show the ./tests seed… a0771d15e6 Explicitly disable buffering for stderr in tests fb424fbba2 Make travis show the ./tests seed by removing stdout buffering and always cat tests.log after a travis run. 22a6031184 Merge #690: Add valgrind check to travis 544002c008 Merge #678: Preventing compiler optimizations in benchmarks without a memory fence dd98cc988f travis: Added a valgrind test without endro and enabled recovery+ecdh b4c1382a87 Add valgrind check to travis 0c774d89e6 Merge #688: Fix ASM setting in travis 5c5f71eea5 Fix ASM setting in travis e2625f8a98 Merge #684: Make no-float policy explicit bae1bea3c4 Make no-float policy explicit 78c3836341 Add SECURITY.md 362bb25608 Modified bench_scalar_split so it won't get optimized out 73a30c6b58 Added accumulators and checks on benchmarks so they won't get optimized out 770b3dcd6f Merge #677: Remove note about heap allocation in secp256k1_ecmult_odd_multiples_table_storage_var b76142ff25 Remove note about heap allocation in secp256k1_ecmult_odd_multiples_table_storage_var which was removed in 47045270fa90f81205d989f7107769bce1e71c4d 137d304a6b Merge #647: Increase robustness against UB in secp256k1_scalar_cadd_bit 0d9540b13f Merge #664: Remove mention of ec_privkey_export because it doesn't exist 59782c68b4 Remove mention of ec_privkey_export because it doesn't exist 96cd94e385 Merge #337: variable sized precomputed table for signing dcb2e3b3ff variable signing precompute table b4bff99028 Merge #661: Make ./configure string consistent a467047e11 Make ./configure string consistent e729cc7f5a Merge #657: Fix a nit in the recovery tests b64a2e2597 Fix a nit in the recovery tests e028aa33d3 Merge #650: secp256k1/src/tests.c: Properly handle sscanf return value f1e11d363d Merge #654: Fix typo (∞) ef83281c3a Merge pull request #656 from real-or-random/patch-1 556caad2ca Fix typo in docs for _context_set_illegal_callback 0d82732a9a Improve VERIFY_CHECK of overflow in secp256k1_scalar_cadd_bit. This added check ensures that any curve order overflow doesn't go undetected due a uint32_t overflow. 786dfb49f5 Merge #583: JNI: fix use sig array e95f8ab098 Merge #644: Avoid optimizing out a verify_check 384f55606a Merge #652: README.md: update instruction to run tests ee56accd47 Merge #651: Fix typo in secp256k1_preallocated.h 7b9b117230 Merge #640: scalar_impl.h: fix includes d99bec2e21 Merge #655: jni: Use only Guava for hex encoding and decoding 2abcf951af jni: Use only Guava for hex encoding and decoding 271582b3b7 Fix typo 60f7f2de5d Don't assume that ALIGNMENT > 1 in tests ada6361dec Use ROUND_TO_ALIGN in scratch_create 8ecc6ce50e Add check preventing rounding to alignment from wrapping around in scratch_alloc 4edaf06fb0 Add check preventing integer multiplication wrapping around in scratch_max_allocation ce6d438266 README.md: update instruction to run tests b1e68cb8e6 Fix typo in secp256k1_preallocated.h a11c76c59a secp256k1/src/tests.c: Properly handle sscanf return value 8fe63e5654 Increase robustness against UB. Thanks to elichai2 who noted that the literal '1' is a signed integer, and that shifting a signed 32-bit integer by 31 bits causes an overflow and yields undefined behaviour. While 'scalar_low_impl''s 'secp256k1_scalar_cadd_bit' is only used for testing purposes and currently the 'bit' parameter is only 0 or 1, it is better to avoid undefined behaviour in case the used domain of 'secp256k1_scalar_cadd_bit' expands. 94ae7cbf83 Moved a dereference so the null check will be before the dereferencing 2cb73b1064 scalar_impl.h: fix includes fa33017135 Merge #634: Add a descriptive comment for secp256k1_ecmult_const. ee9e68cd30 Add a descriptive comment for secp256k1_ecmult_const. d0d738d32d Merge #631: typo in comment for secp256k1_ec_pubkey_tweak_mul () 6914c25276 typo in comment for secp256k1_ec_pubkey_tweak_mul () e541a90ef6 Merge #629: Avoid calling _is_zero when _set_b32 fails. f34b0c3f35 Merge #630: Note intention of timing sidechannel freeness. 8d1563b0ff Note intention of timing sidechannel freeness. 1669bb2865 Merge #628: Fix ability to compile tests without -DVERIFY. ecc94abcc8 Merge #627: Guard memcmp in tests against mixed size inputs. 544435fc90 Merge #578: Avoid implementation-defined and undefined behavior when dealing with sizes 143dc6e9ee Merge #595: Allow to use external default callbacks e49f7991c2 Add missing #(un)defines to base-config.h 77defd2c3b Add secp256k1_ prefix to default callback functions 908bdce64e Include stdio.h and stdlib.h explicitly in secp256k1.c 5db782e655 Allow usage of external default callbacks 6095a863fa Replace CHECKs for no_precomp ctx by ARG_CHECKs without a return cd473e02c3 Avoid calling secp256k1_*_is_zero when secp256k1_*_set_b32 fails. 6c36de7a33 Merge #600: scratch space: use single allocation 98836b11f0 scratch: replace frames with "checkpoint" system 7623cf2b97 scratch: save a couple bytes of unnecessarily-allocated memory a7a164f2c6 scratch: rename `max_size` to `size`, document that extra will actually be allocated 5a4bc0bb95 scratch: unify allocations c2b028a281 scratch space: thread `error_callback` into all scratch space functions 0be1a4ae62 scratch: add magic bytes to beginning of structure 92a48a764d scratch space: use single allocation 40839e21b9 Merge #592: Use trivial algorithm in ecmult_multi if scratch space is small dcf392027b Fix ability to compile tests without -DVERIFY. a484e0008b Merge #566: Enable context creation in preallocated memory 0522caac8f Explain caller's obligations for preallocated memory 238305fdbb Move _preallocated functions to separate header 695feb6fbd Export _preallocated functions 814cc78d71 Add tests for contexts in preallocated memory ba12dd08da Check arguments of _preallocated functions 5feadde462 Support cloning a context into preallocated memory c4fd5dab45 Switch to a single malloc call ef020de16f Add size constants for preallocated memory 1bf7c056ba Prepare for manual memory management in preallocated memory 248bffb052 Guard memcmp in tests against mixed size inputs. 36698dcfee Merge #596: Make WINDOW_G configurable a61a93ff50 Clean up ./configure help strings 2842dc523e Make WINDOW_G configurable 1a02d6ce51 Merge #626: Revert "Merge #620: Install headers automatically" 662918cb29 Revert "Merge #620: Install headers automatically" 14c7dbd444 Simplify control flow in DER parsing ec8f20babd Avoid out-of-bound pointers and integer overflows in size comparisons 01ee1b3b3c Parse DER-enconded length into a size_t instead of an int 912680ed86 Merge #561: Respect LDFLAGS and #undef STATIC_PRECOMPUTATION if using basic config 91fae3ace0 Merge #620: Install headers automatically 5df77a0eda Merge #533: Make sure we're not using an uninitialized variable in secp256k1_wnaf_const(...) 975e51e0d9 Merge #617: Pass scalar by reference in secp256k1_wnaf_const() 735fbde04e Merge #619: Clear a copied secret key after negation 16e86150d0 Install headers automatically 069870d92a Clear a copied secret key after negation 8979ec0d9a Pass scalar by reference in secp256k1_wnaf_const() 84a808598b Merge #612: Allow field_10x26_arm.s to compile for ARMv7 architecture d4d270a59c Allow field_10x26_arm.s to compile for ARMv7 architecture b19c000063 Merge #607: Use size_t shifts when computing a size_t 4d01bc2d9c Merge #606: travis: Remove unused sudo:false e6d01e9347 Use size_t shifts when computing a size_t 7667532bd7 travis: Remove unused sudo:false 248f046611 Make sure we're not using an uninitialized variable in secp256k1_wnaf_const(...) 9ab96f7b12 Use trivial algorithm in ecmult_multi if scratch space is small ee99f12f3d Merge #599: Switch x86_64 asm to use "i" instead of "n" for immediate values. d58bc93f2c Switch x86_64 asm to use "i" instead of "n" for immediate values. 05362ee042 Merge #597: Add $(COMMON_LIB) to exhaustive tests to fix ARM asm build 83483869ac Add $(COMMON_LIB) to exhaustive tests to fix ARM asm build aa15154a48 Merge #568: Fix integer overflow in ecmult_multi_var when n is large 2277af5ff0 Fix integer overflow in ecmult_multi_var when n is large dbed75d969 Undefine `STATIC_PRECOMPUTATION` if using the basic config 310111e093 Keep LDFLAGS if `--coverage` 85d0e1bcce Merge #591: Make bench_internal obey secp256k1_fe_sqrt's contract wrt aliasing. 14196379ec Merge #580: Add trivial ecmult_multi algorithm which does not require a scratch space a697d82da9 Add trivial ecmult_multi to the benchmark tool bade617417 Add trivial ecmult_multi algorithm. It is selected when no scratch space is given and just multiplies and adds the points. 5545e13dea Merge #584: configure: Use CFLAGS_FOR_BUILD when checking native compiler 20c5869df2 Merge #516: improvements to random seed in src/tests.c b76e45d5d6 Make bench_internal obey secp256k1_fe_sqrt's contract wrt aliasing. 870a977644 Merge #562: Make use of TAG_PUBKEY constants in secp256k1_eckey_pubkey_parse be40c4d0b5 Fixup for C90 mixed declarations. c71dd2c08f Merge #509: Fix algorithm selection in bench_ecmult 6492bf88cc Merge #518: Summarize build options after running configure 0e9ada1941 Merge #567: Correct order of libs returned on pkg-config --libs --static libsecp2… e96901a4b9 Merge #587: Make randomization of a non-signing context a noop 58df8d03ad Merge #511: Portability fix for the configure scripts generated 2ebdad772a Merge #552: Make constants static: 1c131affd3 Merge #551: secp256k1_fe_sqrt: Verify that the arguments don't alias. ba698f883b Merge #539: Assorted minor corrections 949e85b009 Merge #550: Optimize secp256k1_fe_normalize_weak calls. a34bcaadf1 Actually pass CFLAGS_FOR_BUILD and LDFLAGS_FOR_BUILD to linker 2d5f4cebdc configure: Use CFLAGS_FOR_BUILD when checking native compiler b408c6a8b2 Merge #579: Use __GNUC_PREREQ for detecting __builtin_expect 6198375218 Make randomization of a non-signing context a noop 74e2dbd68e JNI: fix use sig array c663397f46 Use __GNUC_PREREQ for detecting __builtin_expect 3cb057f842 Fix possible integer overflow in DER parsing 89a20a8945 Correct order of libs returned on pkg-config --libs --static libsecp256k1 call. d3cb1f95eb Make use of TAG_PUBKEY constants in secp256k1_eckey_pubkey_parse 496c5b43b8 Make constants static: static const secp256k1_ge secp256k1_ge_const_g; static const int CURVE_B; bf8b86cc07 secp256k1_fe_sqrt: Verify that the arguments don't alias. 9bd89c836b Optimize secp256k1_fe_normalize_weak calls. Move secp256k1_fe_normalize_weak calls out of ECMULT_TABLE_GET_GE and ECMULT_TABLE_GET_GE_STORAGE and into secp256k1_ge_globalz_set_table_gej instead. 52ab96fedb clean dependendies in field_*_impl.h deff5edd42 Correct math typos in field_*.h 4efb3f8dd1 Add check that restrict pointers don't alias with all parameters. 3965027c81 Summarize build options in configure script 0f0517369c Fix algorithm selection in bench_ecmult 8b3841c91d fix bug in fread() failure check cddef0c0be tests: add warning message when /dev/urandom fails 270f6c80db Portability fix for the configure scripts generated REVERT: 43dd1f4fe7 Merge pull request #40 from instagibbs/size_t REVERT: 6532fa0a96 Merge pull request #39 from instagibbs/more_builds REVERT: 2b2429dfaa rangeproof: reduce iteration count in unit tests REVERT: 12b0e5dda7 Enable more builds with rest of experimental flags REVERT: 8c444eef6a use proper types for rangeproof min/max REVERT: 53ad841caf Add explanation about how BIP32 unhardened derivation can be used to simplify whitelisting REVERT: 71c5fe0f6e Add comment to explain effect of max_n_iterations in surjectionproof_init REVERT: 85fd42fb7e add unit test for generator and pedersen commitment roundtripping REVERT: 2ccf885419 rangeproof: fix serialization of pedersen commintments REVERT: 60c173b640 rangeproof: verify correctness of pedersen commitments when parsing REVERT: 32d7526cd5 generator: verify correctness of point when parsing REVERT: ae14e8a9d8 rangeproof: check that points deserialize correctly when verifying rangeproof REVERT: 44fe43d757 rangeproof: add fixed vector test case REVERT: e065d7df9f Expose generator in shared library REVERT: fb1ba329aa fix spelling in documentation REVERT: fb75faa147 Test for rejection of trailing bytes in range proofs REVERT: 9b2cf1708d Test for rejection of trailing bytes in surjection proofs REVERT: a3a1800ba6 Reject surjection proofs with trailing garbage REVERT: 0c77ae9a75 Minor bugfix. Wrong length due to NUL character. REVERT: b1f31bc4b6 Add whitelisting benchmark REVERT: 52a9f8f8f3 add whitelist_impl.h to include for dist REVERT: a707865bc5 generator: add API tests REVERT: ec1ef040f5 generator: remove unnecessary ARG_CHECK from generate() REVERT: b0e9aa828f Fix generator makefile REVERT: 526c65499f Fix pedersen_blind_generator_blind_sum return value documentation REVERT: b51886e722 Add n_keys argument to whitelist_verify REVERT: 37c57de083 Fix checks of whitelist serialize/parse arguments REVERT: 9b8a9d91eb whitelist: fix serialize/parse API to take serialized length REVERT: 7f17515609 Fix include/secp256k1_rangeproof.h function argument documentation. REVERT: 0d817020d9 rangeproof: add API tests REVERT: 417bb0643f surjectionproof: rename unit test functions to be more consistent with other modules REVERT: 1e2d5c1a26 surjectionproof: add API unit tests REVERT: 7878a298b2 surjectionproof: tests_impl.h s/assert/CHECK/g REVERT: e609591b66 rangeproof: fix memory leak in unit tests REVERT: 0c17f7972a add surjection proof module REVERT: c174f0c609 Implement ring-signature based whitelist delegation scheme REVERT: a2bc6604f9 rangeproof: several API changes REVERT: 21bfb3c91a Expose generator in pedersen/rangeproof API REVERT: f4620de040 Constant-time generator module REVERT: d46fc3c191 rangeproof: expose sidechannel message field in the signing API REVERT: cf40b1bed2 [RANGEPROOF BREAK] Use quadratic residue for tie break and modularity cleanup REVERT: 6d28767c79 Get rid of precomputed H tables (Pieter Wuille) REVERT: ae1e576f67 Pedersen commitments, borromean ring signatures, and ZK range proofs. REVERT: efc61dc0ca Add 64-bit integer utilities git-subtree-dir: src/secp256k1 git-subtree-split: 0129b77767ea001e5693e39ac6deecea0c461817
This commit is contained in:
parent
624a6153f5
commit
5fbc67e108
110 changed files with 10378 additions and 3721 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -1,14 +1,17 @@
|
|||
bench_inv
|
||||
bench_ecdh
|
||||
bench_ecmult
|
||||
bench_generator
|
||||
bench_rangeproof
|
||||
bench_schnorrsig
|
||||
bench_sign
|
||||
bench_verify
|
||||
bench_schnorr_verify
|
||||
bench_recover
|
||||
bench_internal
|
||||
tests
|
||||
exhaustive_tests
|
||||
gen_context
|
||||
valgrind_ctime_test
|
||||
*.exe
|
||||
*.so
|
||||
*.a
|
||||
|
|
@ -30,6 +33,8 @@ libtool
|
|||
*.lo
|
||||
*.o
|
||||
*~
|
||||
*.log
|
||||
*.trs
|
||||
src/libsecp256k1-config.h
|
||||
src/libsecp256k1-config.h.in
|
||||
src/ecmult_static_context.h
|
||||
|
|
|
|||
102
.travis.yml
102
.travis.yml
|
|
@ -1,71 +1,111 @@
|
|||
language: c
|
||||
sudo: false
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: bionic
|
||||
# Valgrind currently supports upto macOS 10.13, the latest xcode of that version is 10.1
|
||||
osx_image: xcode10.1
|
||||
addons:
|
||||
apt:
|
||||
packages: libgmp-dev
|
||||
packages:
|
||||
- libgmp-dev
|
||||
- valgrind
|
||||
- libtool-bin
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
cache:
|
||||
directories:
|
||||
- src/java/guava/
|
||||
env:
|
||||
global:
|
||||
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no GENERATOR=no RANGEPROOF=no WHITELIST=no
|
||||
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
|
||||
- WIDEMUL=auto BIGNUM=auto STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check WITH_VALGRIND=yes RUN_VALGRIND=no EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 GENERATOR=no RANGEPROOF=no WHITELIST=no SCHNORRSIG=no MUSIG=no
|
||||
matrix:
|
||||
- SCALAR=32bit FIELD=32bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes
|
||||
- FIELD=64bit EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes
|
||||
- SCALAR=32bit RECOVERY=yes
|
||||
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
|
||||
- SCALAR=64bit
|
||||
- FIELD=64bit RECOVERY=yes
|
||||
- FIELD=64bit ENDOMORPHISM=yes
|
||||
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
|
||||
- FIELD=64bit ASM=x86_64
|
||||
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
|
||||
- FIELD=32bit ENDOMORPHISM=yes
|
||||
- WIDEMUL=int64 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- WIDEMUL=int128 EXPERIMENTAL=yes RANGEPROOF=yes WHITELIST=yes GENERATOR=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- WIDEMUL=int64 RECOVERY=yes
|
||||
- WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- WIDEMUL=int128
|
||||
- WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- WIDEMUL=int128 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- WIDEMUL=int128 ASM=x86_64
|
||||
- BIGNUM=no
|
||||
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
|
||||
- BIGNUM=no RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes
|
||||
- BIGNUM=no STATICPRECOMPUTATION=no
|
||||
- BUILD=distcheck
|
||||
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
|
||||
- EXTRAFLAGS=CFLAGS=-O0
|
||||
- BUILD=check-java JNI=yes ECDH=yes EXPERIMENTAL=yes
|
||||
- BUILD=distcheck WITH_VALGRIND=no CTIMETEST=no BENCH=no
|
||||
- CPPFLAGS=-DDETERMINISTIC
|
||||
- CFLAGS=-O0 CTIMETEST=no
|
||||
- CFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=no
|
||||
- ECMULTGENPRECISION=2
|
||||
- ECMULTGENPRECISION=8
|
||||
- RUN_VALGRIND=yes BIGNUM=no ASM=x86_64 ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes EXTRAFLAGS="--disable-openssl-tests" BUILD=
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- compiler: clang
|
||||
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
|
||||
os: linux
|
||||
env: HOST=i686-linux-gnu
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- libgmp-dev:i386
|
||||
- valgrind
|
||||
- libtool-bin
|
||||
- libc6-dbg:i386
|
||||
- compiler: clang
|
||||
env: HOST=i686-linux-gnu
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- valgrind
|
||||
- libtool-bin
|
||||
- libc6-dbg:i386
|
||||
- compiler: gcc
|
||||
env: HOST=i686-linux-gnu ENDOMORPHISM=yes
|
||||
env: HOST=i686-linux-gnu
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- valgrind
|
||||
- libtool-bin
|
||||
- libc6-dbg:i386
|
||||
- compiler: gcc
|
||||
os: linux
|
||||
env: HOST=i686-linux-gnu
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- libgmp-dev:i386
|
||||
before_install: mkdir -p `dirname $GUAVA_JAR`
|
||||
install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
|
||||
- valgrind
|
||||
- libtool-bin
|
||||
- libc6-dbg:i386
|
||||
# S390x build (big endian system)
|
||||
- compiler: gcc
|
||||
env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes MUSIG=yes CTIMETEST=
|
||||
arch: s390x
|
||||
|
||||
# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
|
||||
# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
|
||||
# https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296
|
||||
before_install:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install gmp valgrind gcc@9; fi
|
||||
|
||||
before_script: ./autogen.sh
|
||||
|
||||
# travis auto terminates jobs that go for 10 minutes without printing to stdout, but travis_wait doesn't work well with forking programs like valgrind (https://docs.travis-ci.com/user/common-build-problems/#build-times-out-because-no-output-was-received https://github.com/bitcoin-core/secp256k1/pull/750#issuecomment-623476860)
|
||||
script:
|
||||
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
|
||||
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
|
||||
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-rangeproof=$RANGEPROOF --enable-module-whitelist=$WHITELIST --enable-module-generator=$GENERATOR --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
os: linux
|
||||
- function keep_alive() { while true; do echo -en "\a"; sleep 60; done }
|
||||
- keep_alive &
|
||||
- ./contrib/travis.sh
|
||||
- kill %keep_alive
|
||||
|
||||
after_script:
|
||||
- cat ./tests.log
|
||||
- cat ./exhaustive_tests.log
|
||||
- cat ./valgrind_ctime_test.log
|
||||
- cat ./bench.log
|
||||
- $CC --version
|
||||
- valgrind --version
|
||||
|
|
|
|||
81
Makefile.am
81
Makefile.am
|
|
@ -1,13 +1,8 @@
|
|||
ACLOCAL_AMFLAGS = -I build-aux/m4
|
||||
|
||||
lib_LTLIBRARIES = libsecp256k1.la
|
||||
if USE_JNI
|
||||
JNI_LIB = libsecp256k1_jni.la
|
||||
noinst_LTLIBRARIES = $(JNI_LIB)
|
||||
else
|
||||
JNI_LIB =
|
||||
endif
|
||||
include_HEADERS = include/secp256k1.h
|
||||
include_HEADERS += include/secp256k1_preallocated.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += src/scalar.h
|
||||
noinst_HEADERS += src/scalar_4x64.h
|
||||
|
|
@ -39,11 +34,11 @@ 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/java/org_bitcoin_NativeSecp256k1.h
|
||||
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
|
||||
noinst_HEADERS += src/assumptions.h
|
||||
noinst_HEADERS += src/util.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
|
||||
|
|
@ -74,16 +69,19 @@ endif
|
|||
|
||||
libsecp256k1_la_SOURCES = src/secp256k1.c
|
||||
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
|
||||
libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB)
|
||||
|
||||
libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
|
||||
libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
|
||||
if VALGRIND_ENABLED
|
||||
libsecp256k1_la_CPPFLAGS += -DVALGRIND
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS =
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult
|
||||
bench_verify_SOURCES = src/bench_verify.c
|
||||
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
# SECP_TEST_INCLUDES are only used here for CRYPTO_CPPFLAGS
|
||||
bench_verify_CPPFLAGS = -DSECP256K1_BUILD $(SECP_TEST_INCLUDES)
|
||||
bench_sign_SOURCES = src/bench_sign.c
|
||||
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
bench_internal_SOURCES = src/bench_internal.c
|
||||
|
|
@ -99,6 +97,12 @@ if USE_TESTS
|
|||
noinst_PROGRAMS += tests
|
||||
tests_SOURCES = src/tests.c
|
||||
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
|
||||
if VALGRIND_ENABLED
|
||||
tests_CPPFLAGS += -DVALGRIND
|
||||
noinst_PROGRAMS += valgrind_ctime_test
|
||||
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
|
||||
valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_LIBS) $(COMMON_LIB)
|
||||
endif
|
||||
if !ENABLE_COVERAGE
|
||||
tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
|
|
@ -114,52 +118,21 @@ exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDE
|
|||
if !ENABLE_COVERAGE
|
||||
exhaustive_tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
exhaustive_tests_LDADD = $(SECP_LIBS)
|
||||
exhaustive_tests_LDADD = $(SECP_LIBS) $(COMMON_LIB)
|
||||
exhaustive_tests_LDFLAGS = -static
|
||||
TESTS += exhaustive_tests
|
||||
endif
|
||||
|
||||
JAVAROOT=src/java
|
||||
JAVAORG=org/bitcoin
|
||||
JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
|
||||
CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
|
||||
JAVA_FILES= \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
|
||||
|
||||
if USE_JNI
|
||||
|
||||
$(JAVA_GUAVA):
|
||||
@echo Guava is missing. Fetch it via: \
|
||||
wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
|
||||
@false
|
||||
|
||||
.stamp-java: $(JAVA_FILES)
|
||||
@echo Compiling $^
|
||||
$(AM_V_at)$(CLASSPATH_ENV) javac $^
|
||||
@touch $@
|
||||
|
||||
if USE_TESTS
|
||||
|
||||
check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
|
||||
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
|
||||
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
|
||||
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src
|
||||
|
||||
gen_context_OBJECTS = gen_context.o
|
||||
gen_context_BIN = gen_context$(BUILD_EXEEXT)
|
||||
gen_%.o: src/gen_%.c
|
||||
gen_%.o: src/gen_%.c src/libsecp256k1-config.h
|
||||
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
|
||||
|
||||
$(gen_context_BIN): $(gen_context_OBJECTS)
|
||||
$(CC_FOR_BUILD) $^ -o $@
|
||||
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
|
||||
|
||||
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
|
||||
$(tests_OBJECTS): src/ecmult_static_context.h
|
||||
|
|
@ -169,15 +142,19 @@ $(bench_ecmult_OBJECTS): src/ecmult_static_context.h
|
|||
src/ecmult_static_context.h: $(gen_context_BIN)
|
||||
./$(gen_context_BIN)
|
||||
|
||||
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
|
||||
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
|
||||
endif
|
||||
|
||||
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
|
||||
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
|
||||
|
||||
if ENABLE_MODULE_ECDH
|
||||
include src/modules/ecdh/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_MUSIG
|
||||
include src/modules/musig/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_RECOVERY
|
||||
include src/modules/recovery/Makefile.am.include
|
||||
endif
|
||||
|
|
@ -197,3 +174,11 @@ endif
|
|||
if ENABLE_MODULE_SURJECTIONPROOF
|
||||
include src/modules/surjection/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
|
||||
|
|
|
|||
67
README.md
67
README.md
|
|
@ -3,17 +3,22 @@ libsecp256k1
|
|||
|
||||
[](https://travis-ci.org/bitcoin-core/secp256k1)
|
||||
|
||||
Optimized C library for EC operations on curve secp256k1.
|
||||
Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1.
|
||||
|
||||
This library is a work in progress and is being used to research best practices. Use at your own risk.
|
||||
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.
|
||||
* Adding/multiplying private/public keys.
|
||||
* Serialization/parsing of private keys, public keys, signatures.
|
||||
* Constant time, constant memory access signing and pubkey generation.
|
||||
* Derandomized DSA (via RFC6979 or with a caller provided function.)
|
||||
* 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.
|
||||
* Optional module for public key recovery.
|
||||
* Optional module for ECDH key exchange.
|
||||
|
||||
Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable.
|
||||
|
||||
Implementation details
|
||||
----------------------
|
||||
|
|
@ -23,11 +28,12 @@ Implementation details
|
|||
* 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.
|
||||
* Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan).
|
||||
* Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman).
|
||||
* Scalar operations
|
||||
* Optimized implementation without data-dependent branches of arithmetic modulo the curve's order.
|
||||
|
|
@ -42,12 +48,14 @@ Implementation details
|
|||
* 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.
|
||||
* Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones.
|
||||
* 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.
|
||||
* Access the table with branch-free conditional moves so memory access is uniform.
|
||||
* No data-dependent branches
|
||||
* The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally.
|
||||
* 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.
|
||||
|
||||
Build steps
|
||||
-----------
|
||||
|
|
@ -57,5 +65,40 @@ libsecp256k1 is built using autotools:
|
|||
$ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
$ ./tests
|
||||
$ make check
|
||||
$ sudo make install # optional
|
||||
|
||||
Exhaustive tests
|
||||
-----------
|
||||
|
||||
$ ./exhaustive_tests
|
||||
|
||||
With valgrind, you might need to increase the max stack size:
|
||||
|
||||
$ valgrind --max-stackframe=2500000 ./exhaustive_tests
|
||||
|
||||
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:
|
||||
|
||||
$ gcovr --exclude 'src/bench*' --html --html-details -o coverage.html
|
||||
|
||||
Reporting a vulnerability
|
||||
------------
|
||||
|
||||
See [SECURITY.md](SECURITY.md)
|
||||
|
|
|
|||
15
SECURITY.md
Normal file
15
SECURITY.md
Normal 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 |
|
||||
| Andrew Poelstra | 699A 63EF C17A D3A9 A34C FFC0 7AD0 A91C 40BD 0091 |
|
||||
| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 |
|
||||
|
||||
You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.
|
||||
3
TODO
3
TODO
|
|
@ -1,3 +0,0 @@
|
|||
* Unit tests for fieldelem/groupelem, including ones intended to
|
||||
trigger fieldelem's boundary cases.
|
||||
* Complete constant-time operations for signing/keygen
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
|
||||
# programs using the JNI interface.
|
||||
#
|
||||
# JNI include directories are usually in the Java distribution. This is
|
||||
# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
|
||||
# that order. When this macro completes, a list of directories is left in
|
||||
# the variable JNI_INCLUDE_DIRS.
|
||||
#
|
||||
# Example usage follows:
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
|
||||
# do
|
||||
# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
|
||||
# done
|
||||
#
|
||||
# If you want to force a specific compiler:
|
||||
#
|
||||
# - at the configure.in level, set JAVAC=yourcompiler before calling
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# - at the configure level, setenv JAVAC
|
||||
#
|
||||
# Note: This macro can work with the autoconf M4 macros for Java programs.
|
||||
# This particular macro is not part of the original set of macros.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 14
|
||||
|
||||
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
|
||||
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
|
||||
|
||||
JNI_INCLUDE_DIRS=""
|
||||
|
||||
if test "x$JAVA_HOME" != x; then
|
||||
_JTOPDIR="$JAVA_HOME"
|
||||
else
|
||||
if test "x$JAVAC" = x; then
|
||||
JAVAC=javac
|
||||
fi
|
||||
AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
|
||||
if test "x$_ACJNI_JAVAC" = xno; then
|
||||
AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
|
||||
fi
|
||||
_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
|
||||
_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
|
||||
fi
|
||||
|
||||
case "$host_os" in
|
||||
darwin*) # Apple Java headers are inside the Xcode bundle.
|
||||
macos_version=$(sw_vers -productVersion | sed -n -e 's/^@<:@0-9@:>@*.\(@<:@0-9@:>@*\).@<:@0-9@:>@*/\1/p')
|
||||
if @<:@ "$macos_version" -gt "7" @:>@; then
|
||||
_JTOPDIR="$(xcrun --show-sdk-path)/System/Library/Frameworks/JavaVM.framework"
|
||||
_JINC="$_JTOPDIR/Headers"
|
||||
else
|
||||
_JTOPDIR="/System/Library/Frameworks/JavaVM.framework"
|
||||
_JINC="$_JTOPDIR/Headers"
|
||||
fi
|
||||
;;
|
||||
*) _JINC="$_JTOPDIR/include";;
|
||||
esac
|
||||
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
|
||||
_AS_ECHO_LOG([_JINC=$_JINC])
|
||||
|
||||
# On Mac OS X 10.6.4, jni.h is a symlink:
|
||||
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
|
||||
# -> ../../CurrentJDK/Headers/jni.h.
|
||||
AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
|
||||
[
|
||||
if test -f "$_JINC/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JINC"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
|
||||
if test -f "$_JTOPDIR/include/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JTOPDIR/include"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
ac_cv_jni_header_path=none
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
# get the likely subdirectories for system specific java includes
|
||||
case "$host_os" in
|
||||
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
|
||||
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
|
||||
darwin*) _JNI_INC_SUBDIRS="darwin";;
|
||||
linux*) _JNI_INC_SUBDIRS="linux genunix";;
|
||||
osf*) _JNI_INC_SUBDIRS="alpha";;
|
||||
solaris*) _JNI_INC_SUBDIRS="solaris";;
|
||||
mingw*) _JNI_INC_SUBDIRS="win32";;
|
||||
cygwin*) _JNI_INC_SUBDIRS="win32";;
|
||||
*) _JNI_INC_SUBDIRS="genunix";;
|
||||
esac
|
||||
|
||||
if test "x$ac_cv_jni_header_path" != "xnone"; then
|
||||
# add any subdirectories that are present
|
||||
for JINCSUBDIR in $_JNI_INC_SUBDIRS
|
||||
do
|
||||
if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
# _ACJNI_FOLLOW_SYMLINKS <path>
|
||||
# Follows symbolic links on <path>,
|
||||
# finally setting variable _ACJNI_FOLLOWED
|
||||
# ----------------------------------------
|
||||
AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
|
||||
# find the include directory relative to the javac executable
|
||||
_cur="$1"
|
||||
while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
|
||||
AC_MSG_CHECKING([symlink for $_cur])
|
||||
_slink=`ls -ld "$_cur" | sed 's/.* -> //'`
|
||||
case "$_slink" in
|
||||
/*) _cur="$_slink";;
|
||||
# 'X' avoids triggering unwanted echo options.
|
||||
*) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
|
||||
esac
|
||||
AC_MSG_RESULT([$_cur])
|
||||
done
|
||||
_ACJNI_FOLLOWED="$_cur"
|
||||
])# _ACJNI
|
||||
|
|
@ -1,8 +1,3 @@
|
|||
dnl libsecp25k1 helper checks
|
||||
AC_DEFUN([SECP_INT128_CHECK],[
|
||||
has_int128=$ac_cv_type___int128
|
||||
])
|
||||
|
||||
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)
|
||||
|
|
@ -38,19 +33,45 @@ AC_DEFUN([SECP_OPENSSL_CHECK],[
|
|||
fi
|
||||
if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
|
||||
AC_MSG_CHECKING(for EC functions in libcrypto)
|
||||
CPPFLAGS_TEMP="$CPPFLAGS"
|
||||
CPPFLAGS="$CRYPTO_CPPFLAGS $CPPFLAGS"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>]],[[
|
||||
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
|
||||
# if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) {(void)sig->r; (void)sig->s;}
|
||||
# endif
|
||||
|
||||
unsigned int zero = 0;
|
||||
const unsigned char *zero_ptr = (unsigned char*)&zero;
|
||||
EC_KEY_free(EC_KEY_new_by_curve_name(NID_secp256k1));
|
||||
EC_KEY *eckey = EC_KEY_new();
|
||||
EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
|
||||
EC_KEY_set_group(eckey, group);
|
||||
ECDSA_sign(0, NULL, 0, NULL, &zero, eckey);
|
||||
ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
|
||||
o2i_ECPublicKey(&eckey, &zero_ptr, 0);
|
||||
d2i_ECPrivateKey(&eckey, &zero_ptr, 0);
|
||||
EC_KEY_check_key(eckey);
|
||||
EC_KEY_free(eckey);
|
||||
EC_GROUP_free(group);
|
||||
ECDSA_SIG *sig_openssl;
|
||||
sig_openssl = ECDSA_SIG_new();
|
||||
d2i_ECDSA_SIG(&sig_openssl, &zero_ptr, 0);
|
||||
i2d_ECDSA_SIG(sig_openssl, NULL);
|
||||
ECDSA_SIG_get0(sig_openssl, NULL, NULL);
|
||||
ECDSA_SIG_free(sig_openssl);
|
||||
const BIGNUM *bignum = BN_value_one();
|
||||
BN_is_negative(bignum);
|
||||
BN_num_bits(bignum);
|
||||
if (sizeof(zero) >= BN_num_bytes(bignum)) {
|
||||
BN_bn2bin(bignum, (unsigned char*)&zero);
|
||||
}
|
||||
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
|
||||
AC_MSG_RESULT([$has_openssl_ec])
|
||||
CPPFLAGS="$CPPFLAGS_TEMP"
|
||||
fi
|
||||
])
|
||||
|
||||
|
|
|
|||
414
configure.ac
414
configure.ac
|
|
@ -7,6 +7,11 @@ AH_TOP([#ifndef LIBSECP256K1_CONFIG_H])
|
|||
AH_TOP([#define LIBSECP256K1_CONFIG_H])
|
||||
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
|
||||
# Set -g if CFLAGS are not already set, which matches the default autoconf
|
||||
# behavior (see PROG_CC in the Autoconf manual) with the exception that we don't
|
||||
# set -O2 here because we set it in any case (see further down).
|
||||
: ${CFLAGS="-g"}
|
||||
LT_INIT
|
||||
|
||||
dnl make the compilation flags quiet unless V=1 is used
|
||||
|
|
@ -19,10 +24,6 @@ AC_PATH_TOOL(RANLIB, ranlib)
|
|||
AC_PATH_TOOL(STRIP, strip)
|
||||
AX_PROG_CC_FOR_BUILD
|
||||
|
||||
if test "x$CFLAGS" = "x"; then
|
||||
CFLAGS="-g"
|
||||
fi
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
AC_PROG_CC_C89
|
||||
|
|
@ -45,6 +46,7 @@ case $host_os in
|
|||
if test x$openssl_prefix != x; then
|
||||
PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export PKG_CONFIG_PATH
|
||||
CRYPTO_CPPFLAGS="-I$openssl_prefix/include"
|
||||
fi
|
||||
if test x$gmp_prefix != x; then
|
||||
GMP_CPPFLAGS="-I$gmp_prefix/include"
|
||||
|
|
@ -63,11 +65,11 @@ case $host_os in
|
|||
;;
|
||||
esac
|
||||
|
||||
CFLAGS="$CFLAGS -W"
|
||||
CFLAGS="-W $CFLAGS"
|
||||
|
||||
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
|
||||
warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $warn_CFLAGS"
|
||||
CFLAGS="$warn_CFLAGS $CFLAGS"
|
||||
AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
|
|
@ -76,7 +78,7 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
|||
])
|
||||
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden"
|
||||
CFLAGS="-fvisibility=hidden $CFLAGS"
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
|
|
@ -85,106 +87,144 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
|||
])
|
||||
|
||||
AC_ARG_ENABLE(benchmark,
|
||||
AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]),
|
||||
AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
|
||||
[use_benchmark=$enableval],
|
||||
[use_benchmark=yes])
|
||||
|
||||
AC_ARG_ENABLE(coverage,
|
||||
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
|
||||
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]),
|
||||
[enable_coverage=$enableval],
|
||||
[enable_coverage=no])
|
||||
|
||||
AC_ARG_ENABLE(tests,
|
||||
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
|
||||
AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]),
|
||||
[use_tests=$enableval],
|
||||
[use_tests=yes])
|
||||
|
||||
AC_ARG_ENABLE(openssl_tests,
|
||||
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
|
||||
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests [default=auto]]),
|
||||
[enable_openssl_tests=$enableval],
|
||||
[enable_openssl_tests=auto])
|
||||
|
||||
AC_ARG_ENABLE(experimental,
|
||||
AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
|
||||
AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]),
|
||||
[use_experimental=$enableval],
|
||||
[use_experimental=no])
|
||||
|
||||
AC_ARG_ENABLE(exhaustive_tests,
|
||||
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
|
||||
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]),
|
||||
[use_exhaustive_tests=$enableval],
|
||||
[use_exhaustive_tests=yes])
|
||||
|
||||
AC_ARG_ENABLE(endomorphism,
|
||||
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
|
||||
[use_endomorphism=$enableval],
|
||||
[use_endomorphism=no])
|
||||
|
||||
AC_ARG_ENABLE(ecmult_static_precomputation,
|
||||
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
|
||||
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]),
|
||||
[use_ecmult_static_precomputation=$enableval],
|
||||
[use_ecmult_static_precomputation=auto])
|
||||
|
||||
AC_ARG_ENABLE(module_ecdh,
|
||||
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
|
||||
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]),
|
||||
[enable_module_ecdh=$enableval],
|
||||
[enable_module_ecdh=no])
|
||||
|
||||
AC_ARG_ENABLE(module_musig,
|
||||
AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]),
|
||||
[enable_module_musig=$enableval],
|
||||
[enable_module_musig=no])
|
||||
|
||||
AC_ARG_ENABLE(module_recovery,
|
||||
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
|
||||
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]),
|
||||
[enable_module_recovery=$enableval],
|
||||
[enable_module_recovery=no])
|
||||
|
||||
AC_ARG_ENABLE(module_generator,
|
||||
AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module (default is no)]),
|
||||
AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]),
|
||||
[enable_module_generator=$enableval],
|
||||
[enable_module_generator=no])
|
||||
|
||||
AC_ARG_ENABLE(module_rangeproof,
|
||||
AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module (default is no)]),
|
||||
AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]),
|
||||
[enable_module_rangeproof=$enableval],
|
||||
[enable_module_rangeproof=no])
|
||||
|
||||
AC_ARG_ENABLE(module_whitelist,
|
||||
AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module (default is no)]),
|
||||
AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]),
|
||||
[enable_module_whitelist=$enableval],
|
||||
[enable_module_whitelist=no])
|
||||
|
||||
AC_ARG_ENABLE(jni,
|
||||
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]),
|
||||
[use_jni=$enableval],
|
||||
[use_jni=no])
|
||||
AC_ARG_ENABLE(module_extrakeys,
|
||||
AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]),
|
||||
[enable_module_extrakeys=$enableval],
|
||||
[enable_module_extrakeys=no])
|
||||
|
||||
AC_ARG_ENABLE(module_schnorrsig,
|
||||
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
|
||||
[enable_module_schnorrsig=$enableval],
|
||||
[enable_module_schnorrsig=no])
|
||||
|
||||
AC_ARG_ENABLE(external_default_callbacks,
|
||||
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
|
||||
[use_external_default_callbacks=$enableval],
|
||||
[use_external_default_callbacks=no])
|
||||
|
||||
AC_ARG_ENABLE(module_surjectionproof,
|
||||
AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module (default is no)]),
|
||||
AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]),
|
||||
[enable_module_surjectionproof=$enableval],
|
||||
[enable_module_surjectionproof=no])
|
||||
|
||||
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
|
||||
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
|
||||
AC_ARG_ENABLE(reduced_surjection_proof_size,
|
||||
AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]),
|
||||
[use_reduced_surjection_proof_size=$enableval],
|
||||
[use_reduced_surjection_proof_size=no])
|
||||
|
||||
dnl Test-only override of the (autodetected by the C code) "widemul" setting.
|
||||
dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
|
||||
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
|
||||
|
||||
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
|
||||
[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto])
|
||||
[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
|
||||
|
||||
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
|
||||
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=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([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
|
||||
[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[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.]
|
||||
["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
|
||||
)],
|
||||
[req_ecmult_window=$withval], [req_ecmult_window=auto])
|
||||
|
||||
AC_CHECK_TYPES([__int128])
|
||||
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_MSG_CHECKING([for __builtin_expect])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
|
||||
[ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
])
|
||||
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])
|
||||
|
||||
if test x"$req_valgrind" = x"no"; then
|
||||
enable_valgrind=no
|
||||
else
|
||||
AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [
|
||||
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
|
||||
], [])
|
||||
fi
|
||||
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
|
||||
|
||||
if test x"$enable_coverage" = x"yes"; then
|
||||
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
|
||||
CFLAGS="$CFLAGS -O0 --coverage"
|
||||
LDFLAGS="--coverage"
|
||||
CFLAGS="-O0 --coverage $CFLAGS"
|
||||
LDFLAGS="--coverage $LDFLAGS"
|
||||
else
|
||||
CFLAGS="$CFLAGS -O3"
|
||||
CFLAGS="-O2 $CFLAGS"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for __builtin_popcount])
|
||||
|
|
@ -194,27 +234,54 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_popcount(0);}]])],
|
|||
])
|
||||
|
||||
if test x"$use_ecmult_static_precomputation" != x"no"; then
|
||||
# Temporarily switch to an environment for the native compiler
|
||||
save_cross_compiling=$cross_compiling
|
||||
cross_compiling=no
|
||||
TEMP_CC="$CC"
|
||||
SAVE_CC="$CC"
|
||||
CC="$CC_FOR_BUILD"
|
||||
AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
|
||||
SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS_FOR_BUILD"
|
||||
SAVE_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS_FOR_BUILD"
|
||||
SAVE_LDFLAGS="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS_FOR_BUILD"
|
||||
|
||||
warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
|
||||
AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
|
||||
[ AC_MSG_RESULT([yes]) ],
|
||||
[ AC_MSG_RESULT([no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
])
|
||||
|
||||
AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([], [return 0])],
|
||||
[AC_LANG_PROGRAM([], [])],
|
||||
[working_native_cc=yes],
|
||||
[working_native_cc=no],[dnl])
|
||||
CC="$TEMP_CC"
|
||||
[working_native_cc=no],[:])
|
||||
|
||||
CFLAGS_FOR_BUILD="$CFLAGS"
|
||||
|
||||
# Restore the environment
|
||||
cross_compiling=$save_cross_compiling
|
||||
CC="$SAVE_CC"
|
||||
CFLAGS="$SAVE_CFLAGS"
|
||||
CPPFLAGS="$SAVE_CPPFLAGS"
|
||||
LDFLAGS="$SAVE_LDFLAGS"
|
||||
|
||||
if test x"$working_native_cc" = x"no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
set_precomp=no
|
||||
m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
|
||||
if test x"$use_ecmult_static_precomputation" = x"yes"; then
|
||||
AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
|
||||
else
|
||||
AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([ok])
|
||||
AC_MSG_RESULT([yes])
|
||||
set_precomp=yes
|
||||
fi
|
||||
else
|
||||
|
|
@ -254,63 +321,6 @@ else
|
|||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_field" = x"auto"; then
|
||||
if test x"set_asm" = x"x86_64"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_field=64bit
|
||||
fi
|
||||
fi
|
||||
if test x"$set_field" = x; then
|
||||
set_field=32bit
|
||||
fi
|
||||
else
|
||||
set_field=$req_field
|
||||
case $set_field in
|
||||
64bit)
|
||||
if test x"$set_asm" != x"x86_64"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation selection])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_scalar" = x"auto"; then
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" = x"yes"; then
|
||||
set_scalar=64bit
|
||||
fi
|
||||
if test x"$set_scalar" = x; then
|
||||
set_scalar=32bit
|
||||
fi
|
||||
else
|
||||
set_scalar=$req_scalar
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
SECP_INT128_CHECK
|
||||
if test x"$has_int128" != x"yes"; then
|
||||
AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available])
|
||||
fi
|
||||
;;
|
||||
32bit)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation selected])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x"$req_bignum" = x"auto"; then
|
||||
SECP_GMP_CHECK
|
||||
if test x"$has_gmp" = x"yes"; then
|
||||
|
|
@ -354,16 +364,18 @@ no)
|
|||
;;
|
||||
esac
|
||||
|
||||
# select field implementation
|
||||
case $set_field in
|
||||
64bit)
|
||||
AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation])
|
||||
# select wide multiplication implementation
|
||||
case $set_widemul in
|
||||
int128)
|
||||
AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation])
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation])
|
||||
int64)
|
||||
AC_DEFINE(USE_FORCE_WIDEMUL_INT64, 1, [Define this symbol to force the use of the (u)int64_t based wide multiplication implementation])
|
||||
;;
|
||||
auto)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid field implementation])
|
||||
AC_MSG_ERROR([invalid wide multiplication implementation])
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
@ -385,25 +397,50 @@ no)
|
|||
;;
|
||||
esac
|
||||
|
||||
#select scalar implementation
|
||||
case $set_scalar in
|
||||
64bit)
|
||||
AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation])
|
||||
;;
|
||||
32bit)
|
||||
AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation])
|
||||
#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)
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([invalid scalar implementation])
|
||||
if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
|
||||
# not in range
|
||||
AC_MSG_ERROR($error_window_size)
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation])
|
||||
;;
|
||||
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)
|
||||
AC_DEFINE_UNQUOTED(ECMULT_GEN_PREC_BITS, $set_ecmult_gen_precision, [Set ecmult gen precision bits])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(['ecmult gen precision not 2, 4, 8 or "auto"'])
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$use_tests" = x"yes"; then
|
||||
SECP_OPENSSL_CHECK
|
||||
if test x"$has_openssl_ec" = x"yes"; then
|
||||
if test x"$enable_openssl_tests" != x"no"; then
|
||||
if test x"$enable_openssl_tests" != x"no" && test x"$has_openssl_ec" = x"yes"; then
|
||||
enable_openssl_tests=yes
|
||||
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
|
||||
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
|
||||
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS $CRYPTO_CPPFLAGS"
|
||||
SECP_TEST_LIBS="$CRYPTO_LIBS"
|
||||
|
||||
case $host in
|
||||
|
|
@ -411,39 +448,17 @@ if test x"$use_tests" = x"yes"; then
|
|||
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
|
||||
fi
|
||||
enable_openssl_tests=no
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$use_jni" != x"no"; then
|
||||
AX_JNI_INCLUDE_DIR
|
||||
have_jni_dependencies=yes
|
||||
if test x"$enable_module_ecdh" = x"no"; then
|
||||
have_jni_dependencies=no
|
||||
fi
|
||||
if test "x$JNI_INCLUDE_DIRS" = "x"; then
|
||||
have_jni_dependencies=no
|
||||
fi
|
||||
if test "x$have_jni_dependencies" = "xno"; then
|
||||
if test x"$use_jni" = x"yes"; then
|
||||
AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
|
||||
fi
|
||||
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
|
||||
use_jni=no
|
||||
else
|
||||
use_jni=yes
|
||||
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
|
||||
JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
|
||||
done
|
||||
fi
|
||||
enable_openssl_tests=no
|
||||
fi
|
||||
|
||||
if test x"$set_bignum" = x"gmp"; then
|
||||
|
|
@ -451,10 +466,6 @@ if test x"$set_bignum" = x"gmp"; then
|
|||
SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS"
|
||||
fi
|
||||
|
||||
if test x"$use_endomorphism" = x"yes"; then
|
||||
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
|
||||
fi
|
||||
|
||||
if test x"$set_precomp" = x"yes"; then
|
||||
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
|
||||
fi
|
||||
|
|
@ -463,6 +474,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
|
|||
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
|
||||
fi
|
||||
|
||||
if test x"$enable_module_musig" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module])
|
||||
fi
|
||||
|
||||
if test x"$enable_module_recovery" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
|
||||
fi
|
||||
|
|
@ -483,35 +498,49 @@ if test x"$enable_module_surjectionproof" = x"yes"; then
|
|||
AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module])
|
||||
fi
|
||||
|
||||
AC_C_BIGENDIAN()
|
||||
if test x"$enable_module_schnorrsig" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
|
||||
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
|
||||
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
|
||||
fi
|
||||
|
||||
if test x"$use_external_asm" = x"yes"; then
|
||||
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
|
||||
fi
|
||||
|
||||
AC_MSG_NOTICE([Using static precomputation: $set_precomp])
|
||||
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
|
||||
AC_MSG_NOTICE([Using field implementation: $set_field])
|
||||
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
|
||||
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
|
||||
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
|
||||
AC_MSG_NOTICE([Building benchmarks: $use_benchmark])
|
||||
AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
|
||||
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
|
||||
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
|
||||
AC_MSG_NOTICE([Using jni: $use_jni])
|
||||
if test x"$use_external_default_callbacks" = x"yes"; then
|
||||
AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
|
||||
fi
|
||||
|
||||
if test x"$use_reduced_surjection_proof_size" = x"yes"; then
|
||||
AC_DEFINE(USE_REDUCED_SURJECTION_PROOF_SIZE, 1, [Define this symbol to reduce SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS to 16, disabling parsing and verification])
|
||||
fi
|
||||
|
||||
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([Building ECDH module: $enable_module_ecdh])
|
||||
AC_MSG_NOTICE([Building NUMS generator module: $enable_module_generator])
|
||||
AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof])
|
||||
AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist])
|
||||
AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof])
|
||||
AC_MSG_NOTICE([Building MuSig module: $enable_module_musig])
|
||||
AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
|
||||
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
|
||||
AC_MSG_NOTICE([******])
|
||||
|
||||
|
||||
if test x"$enable_module_schnorrsig" != x"yes"; then
|
||||
if test x"$enable_module_musig" = x"yes"; then
|
||||
AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$enable_module_generator" != x"yes"; then
|
||||
if test x"$enable_module_rangeproof" = x"yes"; then
|
||||
AC_MSG_ERROR([Rangeproof module requires the generator module. Use --enable-module-generator to allow.])
|
||||
|
|
@ -527,8 +556,14 @@ if test x"$enable_experimental" = x"yes"; then
|
|||
fi
|
||||
fi
|
||||
else
|
||||
if test x"$enable_module_ecdh" = x"yes"; then
|
||||
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
|
||||
if test x"$enable_module_musig" = x"yes"; then
|
||||
AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
if test x"$enable_module_extrakeys" = x"yes"; then
|
||||
AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
if test x"$enable_module_schnorrsig" = x"yes"; then
|
||||
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
if test x"$set_asm" = x"arm"; then
|
||||
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
|
||||
|
|
@ -549,7 +584,6 @@ fi
|
|||
|
||||
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
|
||||
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
|
||||
AC_SUBST(JNI_INCLUDES)
|
||||
AC_SUBST(SECP_INCLUDES)
|
||||
AC_SUBST(SECP_LIBS)
|
||||
AC_SUBST(SECP_TEST_LIBS)
|
||||
|
|
@ -560,14 +594,17 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
|
|||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_WHITELIST], [test x"$enable_module_whitelist" = x"yes"])
|
||||
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == 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"$use_external_asm" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_SURJECTIONPROOF], [test x"$enable_module_surjectionproof" = x"yes"])
|
||||
AM_CONDITIONAL([USE_REDUCED_SURJECTION_PROOF_SIZE], [test x"$use_reduced_surjection_proof_size" = x"yes"])
|
||||
|
||||
dnl make sure nothing new is exported so that we don't break the cache
|
||||
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
|
||||
|
|
@ -575,3 +612,32 @@ unset PKG_CONFIG_PATH
|
|||
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
echo
|
||||
echo "Build Options:"
|
||||
echo " with ecmult precomp = $set_precomp"
|
||||
echo " with external callbacks = $use_external_default_callbacks"
|
||||
echo " with benchmarks = $use_benchmark"
|
||||
echo " with tests = $use_tests"
|
||||
echo " with openssl tests = $enable_openssl_tests"
|
||||
echo " with coverage = $enable_coverage"
|
||||
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 " bignum = $set_bignum"
|
||||
echo " ecmult window size = $set_ecmult_window"
|
||||
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
|
||||
dnl 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 " CFLAGS = $CFLAGS"
|
||||
echo " CPPFLAGS = $CPPFLAGS"
|
||||
echo " LDFLAGS = $LDFLAGS"
|
||||
echo
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
pos += lenbyte;
|
||||
|
|
@ -51,7 +51,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
while (lenbyte > 0 && input[pos] == 0) {
|
||||
|
|
@ -89,7 +89,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
lenbyte = input[pos++];
|
||||
if (lenbyte & 0x80) {
|
||||
lenbyte -= 0x80;
|
||||
if (pos + lenbyte > inputlen) {
|
||||
if (lenbyte > inputlen - pos) {
|
||||
return 0;
|
||||
}
|
||||
while (lenbyte > 0 && input[pos] == 0) {
|
||||
|
|
@ -112,7 +112,6 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
return 0;
|
||||
}
|
||||
spos = pos;
|
||||
pos += slen;
|
||||
|
||||
/* Ignore leading zeroes in R */
|
||||
while (rlen > 0 && input[rpos] == 0) {
|
||||
|
|
|
|||
69
contrib/travis.sh
Executable file
69
contrib/travis.sh
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ "$HOST" = "i686-linux-gnu" ]
|
||||
then
|
||||
export CC="$CC -m32"
|
||||
fi
|
||||
if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$TRAVIS_COMPILER" = "gcc" ]
|
||||
then
|
||||
export CC="gcc-9"
|
||||
fi
|
||||
|
||||
./configure \
|
||||
--enable-experimental="$EXPERIMENTAL" \
|
||||
--with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
|
||||
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
|
||||
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
|
||||
--enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \
|
||||
--enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG"\
|
||||
--with-valgrind="$WITH_VALGRIND" \
|
||||
--host="$HOST" $EXTRAFLAGS
|
||||
|
||||
if [ -n "$BUILD" ]
|
||||
then
|
||||
make -j2 "$BUILD"
|
||||
fi
|
||||
if [ "$RUN_VALGRIND" = "yes" ]
|
||||
then
|
||||
make -j2
|
||||
# the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (http://valgrind.org/docs/manual/manual-core.html)
|
||||
valgrind --error-exitcode=42 ./tests 16
|
||||
valgrind --error-exitcode=42 ./exhaustive_tests
|
||||
fi
|
||||
if [ "$BENCH" = "yes" ]
|
||||
then
|
||||
if [ "$RUN_VALGRIND" = "yes" ]
|
||||
then
|
||||
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
|
||||
EXEC='./libtool --mode=execute valgrind --error-exitcode=42'
|
||||
else
|
||||
EXEC=
|
||||
fi
|
||||
# This limits the iterations in the benchmarks below to ITER(set in .travis.yml) iterations.
|
||||
export SECP256K1_BENCH_ITERS="$ITERS"
|
||||
{
|
||||
$EXEC ./bench_ecmult
|
||||
$EXEC ./bench_internal
|
||||
$EXEC ./bench_sign
|
||||
$EXEC ./bench_verify
|
||||
} >> bench.log 2>&1
|
||||
if [ "$RECOVERY" = "yes" ]
|
||||
then
|
||||
$EXEC ./bench_recover >> bench.log 2>&1
|
||||
fi
|
||||
if [ "$ECDH" = "yes" ]
|
||||
then
|
||||
$EXEC ./bench_ecdh >> bench.log 2>&1
|
||||
fi
|
||||
if [ "$SCHNORRSIG" = "yes" ]
|
||||
then
|
||||
$EXEC ./bench_schnorrsig >> bench.log 2>&1
|
||||
fi
|
||||
fi
|
||||
if [ "$CTIMETEST" = "yes" ]
|
||||
then
|
||||
./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
|
||||
fi
|
||||
|
|
@ -14,7 +14,7 @@ extern "C" {
|
|||
* 2. Array lengths always immediately the 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, private nonces,
|
||||
* 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,
|
||||
|
|
@ -33,9 +33,10 @@ extern "C" {
|
|||
* verification).
|
||||
*
|
||||
* A constructed context can safely be used from multiple threads
|
||||
* simultaneously, but API call that take a non-const pointer to a context
|
||||
* 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 and secp256k1_context_randomize.
|
||||
* 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.
|
||||
|
|
@ -133,7 +134,7 @@ typedef int (*secp256k1_nonce_function)(
|
|||
# else
|
||||
# define SECP256K1_API
|
||||
# endif
|
||||
# elif defined(__GNUC__) && defined(SECP256K1_BUILD)
|
||||
# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
|
||||
# define SECP256K1_API __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define SECP256K1_API
|
||||
|
|
@ -161,14 +162,17 @@ typedef int (*secp256k1_nonce_function)(
|
|||
/** 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)
|
||||
|
||||
/** Flags to pass to secp256k1_context_create. */
|
||||
/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
|
||||
* secp256k1_context_preallocated_create. */
|
||||
#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)
|
||||
#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
|
||||
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
|
||||
|
||||
/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */
|
||||
/** 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)
|
||||
|
||||
|
|
@ -186,7 +190,11 @@ typedef int (*secp256k1_nonce_function)(
|
|||
*/
|
||||
SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
|
||||
|
||||
/** Create a secp256k1 context object.
|
||||
/** 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 the functions in secp256k1_preallocated.h.
|
||||
*
|
||||
* Returns: a newly created context object.
|
||||
* In: flags: which parts of the context to initialize.
|
||||
|
|
@ -197,7 +205,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create(
|
|||
unsigned int flags
|
||||
) SECP256K1_WARN_UNUSED_RESULT;
|
||||
|
||||
/** Copies a secp256k1 context object.
|
||||
/** 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.
|
||||
*
|
||||
* Returns: a newly created context object.
|
||||
* Args: ctx: an existing context to copy (cannot be NULL)
|
||||
|
|
@ -206,10 +218,18 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone(
|
|||
const secp256k1_context* ctx
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
|
||||
|
||||
/** Destroy a secp256k1 context object.
|
||||
/** Destroy a secp256k1 context object (created in dynamically allocated memory).
|
||||
*
|
||||
* The context pointer may not be used afterwards.
|
||||
* Args: ctx: an existing context to destroy (cannot be NULL)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
SECP256K1_API void secp256k1_context_destroy(
|
||||
secp256k1_context* ctx
|
||||
|
|
@ -229,11 +249,28 @@ SECP256K1_API void secp256k1_context_destroy(
|
|||
* 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 (cannot be NULL)
|
||||
* 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 a default handler that calls abort).
|
||||
* 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.
|
||||
*
|
||||
* See also secp256k1_context_set_error_callback.
|
||||
*/
|
||||
SECP256K1_API void secp256k1_context_set_illegal_callback(
|
||||
secp256k1_context* ctx,
|
||||
|
|
@ -253,9 +290,12 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
|
|||
*
|
||||
* Args: ctx: an existing context object (cannot be NULL)
|
||||
* In: fun: a pointer to a function to call when an internal error occurs,
|
||||
* taking a message and an opaque pointer (NULL restores a default
|
||||
* handler that calls abort).
|
||||
* 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.
|
||||
*
|
||||
* See also secp256k1_context_set_illegal_callback.
|
||||
*/
|
||||
SECP256K1_API void secp256k1_context_set_error_callback(
|
||||
secp256k1_context* ctx,
|
||||
|
|
@ -267,21 +307,24 @@ SECP256K1_API void secp256k1_context_set_error_callback(
|
|||
*
|
||||
* Returns: a newly created scratch space.
|
||||
* Args: ctx: an existing context object (cannot be NULL)
|
||||
* In: max_size: maximum amount of memory to allocate
|
||||
* 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 max_size
|
||||
size_t size
|
||||
) SECP256K1_ARG_NONNULL(1);
|
||||
|
||||
/** Destroy a secp256k1 scratch space.
|
||||
*
|
||||
* The pointer may not be used afterwards.
|
||||
* Args: scratch: space to destroy
|
||||
* 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.
|
||||
*
|
||||
|
|
@ -488,7 +531,7 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
|
|||
/** Create an ECDSA signature.
|
||||
*
|
||||
* Returns: 1: signature created
|
||||
* 0: the nonce generation function failed, or the private key was invalid.
|
||||
* 0: the nonce generation function failed, or the secret key was invalid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
|
||||
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
|
||||
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
|
||||
|
|
@ -509,6 +552,11 @@ SECP256K1_API int secp256k1_ecdsa_sign(
|
|||
) 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
|
||||
|
|
@ -526,7 +574,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify(
|
|||
* 0: secret was invalid, try again
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
|
||||
* Out: pubkey: pointer to the created public key (cannot be NULL)
|
||||
* In: seckey: pointer to a 32-byte private key (cannot be NULL)
|
||||
* In: seckey: pointer to a 32-byte secret key (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
|
||||
const secp256k1_context* ctx,
|
||||
|
|
@ -534,12 +582,24 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
|
|||
const unsigned char *seckey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Negates a private key in place.
|
||||
/** Negates a secret key in place.
|
||||
*
|
||||
* Returns: 1 always
|
||||
* Args: ctx: pointer to a context object
|
||||
* In/Out: seckey: pointer to the 32-byte private key to be negated (cannot be NULL)
|
||||
* 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. (cannot be
|
||||
* NULL)
|
||||
*/
|
||||
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
|
||||
|
|
@ -556,15 +616,29 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate(
|
|||
secp256k1_pubkey *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Tweak a private key by adding tweak to it.
|
||||
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
|
||||
* uniformly random 32-byte arrays, or if the resulting private key
|
||||
* would be invalid (only when the tweak is the complement of the
|
||||
* private key). 1 otherwise.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL).
|
||||
* In/Out: seckey: pointer to a 32-byte private key.
|
||||
* In: tweak: pointer to a 32-byte tweak.
|
||||
/** 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 (cannot be NULL).
|
||||
* 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. (cannot be NULL)
|
||||
* In: tweak: 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) (cannot be NULL).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *seckey,
|
||||
const unsigned char *tweak
|
||||
) 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,
|
||||
|
|
@ -572,14 +646,18 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
|
|||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Tweak a public key by adding tweak times the generator to it.
|
||||
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
|
||||
* uniformly random 32-byte arrays, or if the resulting public key
|
||||
* would be invalid (only when the tweak is the complement of the
|
||||
* corresponding private key). 1 otherwise.
|
||||
* Args: ctx: pointer to a context object initialized for validation
|
||||
*
|
||||
* 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 initialized for validation
|
||||
* (cannot be NULL).
|
||||
* In/Out: pubkey: pointer to a public key object.
|
||||
* In: tweak: pointer to a 32-byte tweak.
|
||||
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
|
||||
* invalid value if this function returns 0 (cannot be NULL).
|
||||
* In: tweak: 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) (cannot be NULL).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
|
||||
const secp256k1_context* ctx,
|
||||
|
|
@ -587,13 +665,27 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add(
|
|||
const unsigned char *tweak
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Tweak a private key by multiplying it by a tweak.
|
||||
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
|
||||
* uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL).
|
||||
* In/Out: seckey: pointer to a 32-byte private key.
|
||||
* In: tweak: pointer to a 32-byte tweak.
|
||||
/** 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 (cannot be NULL).
|
||||
* 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. (cannot be NULL)
|
||||
* In: tweak: 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) (cannot be NULL).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *seckey,
|
||||
const unsigned char *tweak
|
||||
) 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,
|
||||
|
|
@ -601,12 +693,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
|
|||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Tweak a public key by multiplying it by a tweak value.
|
||||
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
|
||||
* uniformly random 32-byte arrays, or equal to zero. 1 otherwise.
|
||||
* Args: ctx: pointer to a context object initialized for validation
|
||||
* (cannot be NULL).
|
||||
* In/Out: pubkey: pointer to a public key obkect.
|
||||
* In: tweak: pointer to a 32-byte tweak.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid. 1 otherwise.
|
||||
* Args: ctx: pointer to a context object initialized for validation
|
||||
* (cannot be NULL).
|
||||
* In/Out: pubkey: pointer to a public key object. pubkey will be set to an
|
||||
* invalid value if this function returns 0 (cannot be NULL).
|
||||
* In: tweak: 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) (cannot be NULL).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
|
||||
const secp256k1_context* ctx,
|
||||
|
|
@ -615,7 +711,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
|
|||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Updates the context randomization to protect against side-channel leakage.
|
||||
* Returns: 1: randomization successfully updated
|
||||
* Returns: 1: randomization successfully updated or nothing to randomize
|
||||
* 0: error
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state)
|
||||
|
|
@ -630,8 +726,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
|
|||
* that it does not affect function results, but shields against attacks which
|
||||
* rely on any input-dependent behaviour.
|
||||
*
|
||||
* This function has currently an effect only on contexts initialized for signing
|
||||
* because randomization is currently used only for signing. However, this is not
|
||||
* guaranteed and may change in the future. It is safe to call this function on
|
||||
* contexts not initialized for signing; then it will have no effect and return 1.
|
||||
*
|
||||
* You should call this after secp256k1_context_create or
|
||||
* secp256k1_context_clone, and may call this repeatedly afterwards.
|
||||
* secp256k1_context_clone (and secp256k1_context_preallocated_create or
|
||||
* secp256k1_context_clone, resp.), and you may call this repeatedly afterwards.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
|
||||
secp256k1_context* ctx,
|
||||
|
|
@ -639,6 +741,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
|
|||
) 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
|
||||
|
|
|
|||
|
|
@ -7,43 +7,50 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** A pointer to a function that applies hash function to a point
|
||||
/** A pointer to a function that hashes an EC point to obtain an ECDH secret
|
||||
*
|
||||
* Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail
|
||||
* Out: output: pointer to an array to be filled by the function
|
||||
* In: x: pointer to a 32-byte x coordinate
|
||||
* y: pointer to a 32-byte y coordinate
|
||||
* data: Arbitrary data pointer that is passed through
|
||||
* 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 *x,
|
||||
const unsigned char *y,
|
||||
const unsigned char *x32,
|
||||
const unsigned char *y32,
|
||||
void *data
|
||||
);
|
||||
|
||||
/** An implementation of SHA256 hash function that applies to compressed public key. */
|
||||
/** An implementation of SHA256 hash function that applies to compressed public key.
|
||||
* Populates the output parameter with 32 bytes. */
|
||||
SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256;
|
||||
|
||||
/** A default ecdh hash function (currently equal to 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 extern 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)
|
||||
* 0: scalar was invalid (zero or overflow) or hashfp returned 0
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: output: pointer to an array to be filled by the function
|
||||
* Out: output: pointer to an array to be filled by hashfp
|
||||
* In: pubkey: a pointer to a secp256k1_pubkey containing an
|
||||
* initialized public key
|
||||
* privkey: a 32-byte scalar with which to multiply the point
|
||||
* 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
|
||||
* data: Arbitrary data pointer that is passed through
|
||||
* (in which case, 32 bytes will be written to output)
|
||||
* data: arbitrary data pointer that is passed through to hashfp
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *output,
|
||||
const secp256k1_pubkey *pubkey,
|
||||
const unsigned char *privkey,
|
||||
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);
|
||||
|
|
|
|||
236
include/secp256k1_extrakeys.h
Normal file
236
include/secp256k1_extrakeys.h
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#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, or
|
||||
* comparison, use secp256k1_xonly_pubkey_serialize and
|
||||
* secp256k1_xonly_pubkey_parse.
|
||||
*/
|
||||
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 (cannot be NULL).
|
||||
* 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.
|
||||
* (cannot be NULL).
|
||||
* In: input32: pointer to a serialized xonly_pubkey (cannot be NULL)
|
||||
*/
|
||||
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 (cannot be NULL).
|
||||
* Out: output32: a pointer to a 32-byte array to place the serialized key in
|
||||
* (cannot be NULL).
|
||||
* In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an
|
||||
* initialized public key (cannot be NULL).
|
||||
*/
|
||||
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);
|
||||
|
||||
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
|
||||
*
|
||||
* Returns: 1 if the public key was successfully converted
|
||||
* 0 otherwise
|
||||
*
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: xonly_pubkey: pointer to an x-only public key object for placing the
|
||||
* converted public key (cannot be NULL)
|
||||
* pk_parity: 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. (can be NULL)
|
||||
* In: pubkey: pointer to a public key that is converted (cannot be NULL)
|
||||
*/
|
||||
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 initialized for verification
|
||||
* (cannot be NULL)
|
||||
* Out: output_pubkey: pointer to a public key to store the result. Will be set
|
||||
* to an invalid value if this function returns 0 (cannot
|
||||
* be NULL)
|
||||
* In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
|
||||
* (cannot be NULL).
|
||||
* 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) (cannot be NULL).
|
||||
*/
|
||||
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 initialized for verification
|
||||
* (cannot be NULL)
|
||||
* In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL)
|
||||
* 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 (cannot be NULL)
|
||||
* tweak32: pointer to a 32-byte tweak (cannot be NULL)
|
||||
*/
|
||||
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, initialized for signing (cannot be NULL)
|
||||
* Out: keypair: pointer to the created keypair (cannot be NULL)
|
||||
* In: seckey: pointer to a 32-byte secret key (cannot be NULL)
|
||||
*/
|
||||
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 public key from a keypair.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid. 1 otherwise.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* 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.
|
||||
* (cannot be NULL)
|
||||
* In: keypair: pointer to a keypair (cannot be NULL)
|
||||
*/
|
||||
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: 0 if the arguments are invalid. 1 otherwise.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* 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 (cannot be
|
||||
* NULL).
|
||||
* pk_parity: pointer to an integer that will be set to the pk_parity
|
||||
* argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
|
||||
* In: keypair: pointer to a keypair (cannot be NULL)
|
||||
*/
|
||||
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 initialized for verification
|
||||
* (cannot be NULL)
|
||||
* In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
|
||||
* an invalid value if this function returns 0 (cannot be
|
||||
* NULL).
|
||||
* 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) (cannot be NULL).
|
||||
*/
|
||||
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 */
|
||||
|
|
@ -25,12 +25,12 @@ typedef struct {
|
|||
*
|
||||
* Returns: 1 if input contains a valid generator.
|
||||
* Args: ctx: a secp256k1 context object.
|
||||
* Out: commit: pointer to the output generator object
|
||||
* Out: gen: pointer to the output generator object
|
||||
* In: input: pointer to a 33-byte serialized generator
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_generator* commit,
|
||||
secp256k1_generator* gen,
|
||||
const unsigned char *input
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
|
|
@ -39,12 +39,12 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_generator_parse(
|
|||
* Returns: 1 always.
|
||||
* Args: ctx: a secp256k1 context object.
|
||||
* Out: output: a pointer to a 33-byte byte array
|
||||
* In: commit: a pointer to a generator
|
||||
* In: gen: a pointer to a generator
|
||||
*/
|
||||
SECP256K1_API int secp256k1_generator_serialize(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *output,
|
||||
const secp256k1_generator* commit
|
||||
const secp256k1_generator* gen
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Generate a generator for the curve.
|
||||
|
|
|
|||
488
include/secp256k1_musig.h
Normal file
488
include/secp256k1_musig.h
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
#ifndef SECP256K1_MUSIG_H
|
||||
#define SECP256K1_MUSIG_H
|
||||
|
||||
#include "secp256k1_extrakeys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** This module implements a Schnorr-based multi-signature scheme called MuSig
|
||||
* (https://eprint.iacr.org/2018/068.pdf). It is compatible with bip-schnorr.
|
||||
* There's an example C source file in the module's directory
|
||||
* (src/modules/musig/example.c) that demonstrates how it can be used.
|
||||
*
|
||||
* The documentation in this include file is for reference and may not be sufficient
|
||||
* for users to begin using the library. A full description of API usage can be found
|
||||
* in src/modules/musig/musig.md
|
||||
*/
|
||||
|
||||
/** Data structure containing auxiliary data generated in `pubkey_combine` and
|
||||
* required for `session_*_init`.
|
||||
* Fields:
|
||||
* magic: Set during initialization in `pubkey_combine` to allow
|
||||
* detecting an uninitialized object.
|
||||
* pk_hash: The 32-byte hash of the original public keys
|
||||
* pk_parity: Whether the MuSig-aggregated point was negated when
|
||||
* converting it to the combined xonly pubkey.
|
||||
* is_tweaked: Whether the combined pubkey was tweaked
|
||||
* tweak: If is_tweaked, array with the 32-byte tweak
|
||||
* internal_key_parity: If is_tweaked, the parity of the combined pubkey
|
||||
* before tweaking
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t magic;
|
||||
unsigned char pk_hash[32];
|
||||
int pk_parity;
|
||||
int is_tweaked;
|
||||
unsigned char tweak[32];
|
||||
int internal_key_parity;
|
||||
} secp256k1_musig_pre_session;
|
||||
|
||||
/** Data structure containing data related to a signing session resulting in a single
|
||||
* signature.
|
||||
*
|
||||
* This structure is not opaque, but it MUST NOT be copied or read or written to it
|
||||
* directly. A signer who is online throughout the whole process and can keep this
|
||||
* structure in memory can use the provided API functions for a safe standard
|
||||
* workflow. See https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/
|
||||
* for more details about the risks associated with serializing or deserializing this
|
||||
* structure.
|
||||
*
|
||||
* Fields:
|
||||
* magic: Set in `musig_session_init` to allow detecting an
|
||||
* uninitialized object.
|
||||
* round: Current round of the session
|
||||
* pre_session: Auxiliary data created in `pubkey_combine`
|
||||
* combined_pk: MuSig-computed combined xonly public key
|
||||
* n_signers: Number of signers
|
||||
* msg: The 32-byte message (hash) to be signed
|
||||
* is_msg_set: Whether the above message has been set
|
||||
* has_secret_data: Whether this session object has a signers' secret data; if this
|
||||
* is `false`, it may still be used for verification purposes.
|
||||
* seckey: If `has_secret_data`, the signer's secret key
|
||||
* secnonce: If `has_secret_data`, the signer's secret nonce
|
||||
* nonce: If `has_secret_data`, the signer's public nonce
|
||||
* nonce_commitments_hash: If `has_secret_data` and round >= 1, the hash of all
|
||||
* signers' commitments
|
||||
* combined_nonce: If round >= 2, the summed combined public nonce
|
||||
* combined_nonce_parity: If round >= 2, the parity of the Y coordinate of above
|
||||
* nonce.
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t magic;
|
||||
int round;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
uint32_t n_signers;
|
||||
int is_msg_set;
|
||||
unsigned char msg[32];
|
||||
int has_secret_data;
|
||||
unsigned char seckey[32];
|
||||
unsigned char secnonce[32];
|
||||
secp256k1_xonly_pubkey nonce;
|
||||
int partial_nonce_parity;
|
||||
unsigned char nonce_commitments_hash[32];
|
||||
secp256k1_xonly_pubkey combined_nonce;
|
||||
int combined_nonce_parity;
|
||||
} secp256k1_musig_session;
|
||||
|
||||
/** Data structure containing data on all signers in a single session.
|
||||
*
|
||||
* The workflow for this structure is as follows:
|
||||
*
|
||||
* 1. This structure is initialized with `musig_session_init` or
|
||||
* `musig_session_init_verifier`, which set the `index` field, and zero out
|
||||
* all other fields. The public session is initialized with the signers'
|
||||
* nonce_commitments.
|
||||
*
|
||||
* 2. In a non-public session the nonce_commitments are set with the function
|
||||
* `musig_get_public_nonce`, which also returns the signer's public nonce. This
|
||||
* ensures that the public nonce is not exposed until all commitments have been
|
||||
* received.
|
||||
*
|
||||
* 3. Each individual data struct should be updated with `musig_set_nonce` once a
|
||||
* nonce is available. This function takes a single signer data struct rather than
|
||||
* an array because it may fail in the case that the provided nonce does not match
|
||||
* the commitment. In this case, it is desirable to identify the exact party whose
|
||||
* nonce was inconsistent.
|
||||
*
|
||||
* Fields:
|
||||
* present: indicates whether the signer's nonce is set
|
||||
* index: index of the signer in the MuSig key aggregation
|
||||
* nonce: public nonce, must be a valid curvepoint if the signer is `present`
|
||||
* nonce_commitment: commitment to the nonce, or all-bits zero if a commitment
|
||||
* has not yet been set
|
||||
*/
|
||||
typedef struct {
|
||||
int present;
|
||||
uint32_t index;
|
||||
secp256k1_xonly_pubkey nonce;
|
||||
unsigned char nonce_commitment[32];
|
||||
} secp256k1_musig_session_signer_data;
|
||||
|
||||
/** Opaque data structure that holds a MuSig partial 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 32 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
|
||||
* `musig_partial_signature_serialize` and `musig_partial_signature_parse`
|
||||
* functions.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[32];
|
||||
} secp256k1_musig_partial_signature;
|
||||
|
||||
/** Computes a combined public key and the hash of the given public keys.
|
||||
* Different orders of `pubkeys` result in different `combined_pk`s.
|
||||
*
|
||||
* Returns: 1 if the public keys were successfully combined, 0 otherwise
|
||||
* Args: ctx: pointer to a context object initialized for verification
|
||||
* (cannot be NULL)
|
||||
* scratch: scratch space used to compute the combined pubkey by
|
||||
* multiexponentiation. If NULL, an inefficient algorithm is used.
|
||||
* Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL)
|
||||
* pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in
|
||||
* `musig_session_init` or `musig_pubkey_tweak_add`.
|
||||
* In: pubkeys: input array of public keys to combine. The order is important;
|
||||
* a different order will result in a different combined public
|
||||
* key (cannot be NULL)
|
||||
* n_pubkeys: length of pubkeys array. Must be greater than 0.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_pubkey_combine(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_scratch_space *scratch,
|
||||
secp256k1_xonly_pubkey *combined_pk,
|
||||
secp256k1_musig_pre_session *pre_session,
|
||||
const secp256k1_xonly_pubkey *pubkeys,
|
||||
size_t n_pubkeys
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Tweak an x-only public key by adding the generator multiplied with tweak32
|
||||
* to it. The resulting output_pubkey with the given internal_pubkey and tweak
|
||||
* passes `secp256k1_xonly_pubkey_tweak_test`.
|
||||
*
|
||||
* This function is only useful before initializing a signing session. If you
|
||||
* are only computing a public key, but not intending to create a signature for
|
||||
* it, you can just use `secp256k1_xonly_pubkey_tweak_add`. Can only be called
|
||||
* once with a given pre_session.
|
||||
*
|
||||
* 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 initialized for verification
|
||||
* (cannot be NULL)
|
||||
* pre_session: pointer to a `musig_pre_session` struct initialized in
|
||||
* `musig_pubkey_combine` (cannot be NULL)
|
||||
* Out: output_pubkey: pointer to a public key to store the result. Will be set
|
||||
* to an invalid value if this function returns 0 (cannot
|
||||
* be NULL)
|
||||
* In: internal_pubkey: pointer to the `combined_pk` from
|
||||
* `musig_pubkey_combine` to which the tweak is applied.
|
||||
* (cannot be NULL).
|
||||
* 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) (cannot be NULL).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_pre_session *pre_session,
|
||||
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) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Initializes a signing session for a signer
|
||||
*
|
||||
* Returns: 1: session is successfully initialized
|
||||
* 0: session could not be initialized: secret key or secret nonce overflow
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Out: session: the session structure to initialize (cannot be NULL)
|
||||
* signers: an array of signers' data to be initialized. Array length must
|
||||
* equal to `n_signers` (cannot be NULL)
|
||||
* nonce_commitment32: filled with a 32-byte commitment to the generated nonce
|
||||
* (cannot be NULL)
|
||||
* In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be
|
||||
* NULL). If a non-unique session_id32 was given then a partial
|
||||
* signature will LEAK THE SECRET KEY.
|
||||
* msg32: the 32-byte message to be signed. Shouldn't be NULL unless you
|
||||
* require sharing nonce commitments before the message is known
|
||||
* because it reduces nonce misuse resistance. If NULL, must be
|
||||
* set with `musig_session_get_public_nonce`.
|
||||
* combined_pk: the combined xonly public key of all signers (cannot be NULL)
|
||||
* pre_session: pointer to a musig_pre_session struct after initializing
|
||||
* it with `musig_pubkey_combine` and optionally provided to
|
||||
* `musig_pubkey_tweak_add` (cannot be NULL).
|
||||
* n_signers: length of signers array. Number of signers participating in
|
||||
* the MuSig. Must be greater than 0 and at most 2^32 - 1.
|
||||
* my_index: index of this signer in the signers array. Must be less
|
||||
* than `n_signers`.
|
||||
* seckey: the signer's 32-byte secret key (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_session_init(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_session *session,
|
||||
secp256k1_musig_session_signer_data *signers,
|
||||
unsigned char *nonce_commitment32,
|
||||
const unsigned char *session_id32,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_xonly_pubkey *combined_pk,
|
||||
const secp256k1_musig_pre_session *pre_session,
|
||||
size_t n_signers,
|
||||
size_t my_index,
|
||||
const unsigned char *seckey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11);
|
||||
|
||||
/** Gets the signer's public nonce given a list of all signers' data with
|
||||
* commitments. Called by participating signers after
|
||||
* `secp256k1_musig_session_init` and after all nonce commitments have
|
||||
* been collected
|
||||
*
|
||||
* Returns: 1: public nonce is written in nonce
|
||||
* 0: signer data is missing commitments or session isn't initialized
|
||||
* for signing
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: the signing session to get the nonce from (cannot be NULL)
|
||||
* signers: an array of signers' data initialized with
|
||||
* `musig_session_init`. Array length must equal to
|
||||
* `n_commitments` (cannot be NULL)
|
||||
* Out: nonce32: filled with a 32-byte public nonce which is supposed to be
|
||||
* sent to the other signers and then used in `musig_set nonce`
|
||||
* (cannot be NULL)
|
||||
* In: commitments: array of pointers to 32-byte nonce commitments (cannot be NULL)
|
||||
* n_commitments: the length of commitments and signers array. Must be the total
|
||||
* number of signers participating in the MuSig.
|
||||
* msg32: the 32-byte message to be signed. Must be NULL if already
|
||||
* set with `musig_session_init` otherwise can not be NULL.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_session *session,
|
||||
secp256k1_musig_session_signer_data *signers,
|
||||
unsigned char *nonce32,
|
||||
const unsigned char *const *commitments,
|
||||
size_t n_commitments,
|
||||
const unsigned char *msg32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Initializes a verifier session that can be used for verifying nonce commitments
|
||||
* and partial signatures. It does not have secret key material and therefore can not
|
||||
* be used to create signatures.
|
||||
*
|
||||
* Returns: 1 when session is successfully initialized, 0 otherwise
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: session: the session structure to initialize (cannot be NULL)
|
||||
* signers: an array of signers' data to be initialized. Array length must
|
||||
* equal to `n_signers`(cannot be NULL)
|
||||
* In: msg32: the 32-byte message to be signed (cannot be NULL)
|
||||
* combined_pk: the combined xonly public key of all signers (cannot be NULL)
|
||||
* pre_session: pointer to a musig_pre_session struct from
|
||||
* `musig_pubkey_combine` (cannot be NULL)
|
||||
* pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL)
|
||||
* commitments: array of pointers to 32-byte nonce commitments. Array
|
||||
* length must equal to `n_signers` (cannot be NULL)
|
||||
* n_signers: length of signers and commitments array. Number of signers
|
||||
* participating in the MuSig. Must be greater than 0 and at most
|
||||
* 2^32 - 1.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_session_init_verifier(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_session *session,
|
||||
secp256k1_musig_session_signer_data *signers,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_xonly_pubkey *combined_pk,
|
||||
const secp256k1_musig_pre_session *pre_session,
|
||||
const unsigned char *const *commitments,
|
||||
size_t n_signers
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);
|
||||
|
||||
/** Checks a signer's public nonce against a commitment to said nonce, and update
|
||||
* data structure if they match
|
||||
*
|
||||
* Returns: 1: commitment was valid, data structure updated
|
||||
* 0: commitment was invalid, nothing happened
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* signer: pointer to the signer data to update (cannot be NULL). Must have
|
||||
* been used with `musig_session_get_public_nonce` or initialized
|
||||
* with `musig_session_init_verifier`.
|
||||
* In: nonce32: signer's alleged public nonce (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_session_signer_data *signer,
|
||||
const unsigned char *nonce32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Updates a session with the combined public nonce of all signers. The combined
|
||||
* public nonce is the sum of every signer's public nonce.
|
||||
*
|
||||
* Returns: 1: nonces are successfully combined
|
||||
* 0: a signer's nonce is missing
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: session to update with the combined public nonce (cannot be
|
||||
* NULL)
|
||||
* signers: an array of signers' data, which must have had public nonces
|
||||
* set with `musig_set_nonce`. Array length must equal to `n_signers`
|
||||
* (cannot be NULL)
|
||||
* n_signers: the length of the signers array. Must be the total number of
|
||||
* signers participating in the MuSig.
|
||||
* Out: nonce_parity: if non-NULL, a pointer to an integer that indicates the
|
||||
* parity of the combined public nonce. Used for adaptor
|
||||
* signatures.
|
||||
* adaptor: point to add to the combined public nonce. If NULL, nothing is
|
||||
* added to the combined nonce.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_session_combine_nonces(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_session *session,
|
||||
const secp256k1_musig_session_signer_data *signers,
|
||||
size_t n_signers,
|
||||
int *nonce_parity,
|
||||
const secp256k1_pubkey *adaptor
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize a MuSig partial signature or adaptor signature
|
||||
*
|
||||
* Returns: 1 when the signature could be serialized, 0 otherwise
|
||||
* Args: ctx: a secp256k1 context object
|
||||
* Out: out32: pointer to a 32-byte array to store the serialized signature
|
||||
* In: sig: pointer to the signature
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_signature_serialize(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *out32,
|
||||
const secp256k1_musig_partial_signature* sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Parse and verify a MuSig partial signature.
|
||||
*
|
||||
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
||||
* Args: ctx: a secp256k1 context object
|
||||
* Out: sig: pointer to a signature object
|
||||
* In: in32: pointer to the 32-byte signature to be parsed
|
||||
*
|
||||
* 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_musig_partial_signature_parse(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_partial_signature* sig,
|
||||
const unsigned char *in32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Produces a partial signature
|
||||
*
|
||||
* Returns: 1: partial signature constructed
|
||||
* 0: session in incorrect or inconsistent state
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: active signing session for which the combined nonce has been
|
||||
* computed (cannot be NULL)
|
||||
* Out: partial_sig: partial signature (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_sign(
|
||||
const secp256k1_context* ctx,
|
||||
const secp256k1_musig_session *session,
|
||||
secp256k1_musig_partial_signature *partial_sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Checks that an individual partial signature verifies
|
||||
*
|
||||
* This function is essential when using protocols with adaptor signatures.
|
||||
* However, it is not essential for regular MuSig's, in the sense that if any
|
||||
* partial signatures does not verify, the full signature will also not verify, so the
|
||||
* problem will be caught. But this function allows determining the specific party
|
||||
* who produced an invalid signature, so that signing can be restarted without them.
|
||||
*
|
||||
* Returns: 1: partial signature verifies
|
||||
* 0: invalid signature or bad data
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: active session for which the combined nonce has been computed
|
||||
* (cannot be NULL)
|
||||
* signer: data for the signer who produced this signature (cannot be NULL)
|
||||
* In: partial_sig: signature to verify (cannot be NULL)
|
||||
* pubkey: public key of the signer who produced the signature (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify(
|
||||
const secp256k1_context* ctx,
|
||||
const secp256k1_musig_session *session,
|
||||
const secp256k1_musig_session_signer_data *signer,
|
||||
const secp256k1_musig_partial_signature *partial_sig,
|
||||
const secp256k1_xonly_pubkey *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Combines partial signatures
|
||||
*
|
||||
* Returns: 1: all partial signatures have values in range. Does NOT mean the
|
||||
* resulting signature verifies.
|
||||
* 0: some partial signature are missing or had s or r out of range
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: initialized session for which the combined nonce has been
|
||||
* computed (cannot be NULL)
|
||||
* Out: sig64: complete signature (cannot be NULL)
|
||||
* In: partial_sigs: array of partial signatures to combine (cannot be NULL)
|
||||
* n_sigs: number of signatures in the partial_sigs array
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine(
|
||||
const secp256k1_context* ctx,
|
||||
const secp256k1_musig_session *session,
|
||||
unsigned char *sig64,
|
||||
const secp256k1_musig_partial_signature *partial_sigs,
|
||||
size_t n_sigs
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Converts a partial signature to an adaptor signature by adding a given secret
|
||||
* adaptor.
|
||||
*
|
||||
* Returns: 1: signature and secret adaptor contained valid values
|
||||
* 0: otherwise
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: adaptor_sig: adaptor signature to produce (cannot be NULL)
|
||||
* In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL)
|
||||
* sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot
|
||||
* be NULL)
|
||||
* nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces`
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_partial_sig_adapt(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_musig_partial_signature *adaptor_sig,
|
||||
const secp256k1_musig_partial_signature *partial_sig,
|
||||
const unsigned char *sec_adaptor32,
|
||||
int nonce_parity
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Extracts a secret adaptor from a MuSig, given all parties' partial
|
||||
* signatures. This function will not fail unless given grossly invalid data; if it
|
||||
* is merely given signatures that do not verify, the returned value will be
|
||||
* nonsense. It is therefore important that all data be verified at earlier steps of
|
||||
* any protocol that uses this function.
|
||||
*
|
||||
* Returns: 1: signatures contained valid data such that an adaptor could be extracted
|
||||
* 0: otherwise
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL)
|
||||
* In: sig64: complete 2-of-2 signature (cannot be NULL)
|
||||
* partial_sigs: array of partial signatures (cannot be NULL)
|
||||
* n_partial_sigs: number of elements in partial_sigs array
|
||||
* nonce_parity: the `nonce_parity` output of `musig_session_combine_nonces`
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *sec_adaptor32,
|
||||
const unsigned char *sig64,
|
||||
const secp256k1_musig_partial_signature *partial_sigs,
|
||||
size_t n_partial_sigs,
|
||||
int nonce_parity
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
128
include/secp256k1_preallocated.h
Normal file
128
include/secp256k1_preallocated.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#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 (cannot be NULL)
|
||||
* flags: which parts of the context to initialize.
|
||||
*
|
||||
* 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 (cannot be NULL)
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Returns: a newly created context object.
|
||||
* Args: ctx: an existing context to copy (cannot be NULL)
|
||||
* In: prealloc: a pointer to a rewritable contiguous block of memory of
|
||||
* size at least secp256k1_context_preallocated_size(flags)
|
||||
* bytes, as detailed above (cannot be NULL)
|
||||
*/
|
||||
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 (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API void secp256k1_context_preallocated_destroy(
|
||||
secp256k1_context* ctx
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SECP256K1_PREALLOCATED_H */
|
||||
|
|
@ -55,9 +55,6 @@ SECP256K1_API int secp256k1_pedersen_commitment_serialize(
|
|||
const secp256k1_pedersen_commitment* commit
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Initialize a context for usage with Pedersen commitments. */
|
||||
void secp256k1_pedersen_context_initialize(secp256k1_context* ctx);
|
||||
|
||||
/** Generate a pedersen commitment.
|
||||
* Returns 1: Commitment successfully created.
|
||||
* 0: Error. The blinding factor is larger than the group order
|
||||
|
|
@ -161,9 +158,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_generato
|
|||
size_t n_inputs
|
||||
);
|
||||
|
||||
/** Initialize a context for usage with Pedersen commitments. */
|
||||
void secp256k1_rangeproof_context_initialize(secp256k1_context* ctx);
|
||||
|
||||
/** Verify a proof that a committed value is within a range.
|
||||
* Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs.
|
||||
* 0: Proof failed or other error.
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
|||
/** Create a recoverable ECDSA signature.
|
||||
*
|
||||
* Returns: 1: signature created
|
||||
* 0: the nonce generation function failed, or the private key was invalid.
|
||||
* 0: the nonce generation function failed, or the secret key was invalid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
|
||||
* Out: sig: pointer to an array where the signature will be placed (cannot be NULL)
|
||||
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
|
||||
|
|
|
|||
111
include/secp256k1_schnorrsig.h
Normal file
111
include/secp256k1_schnorrsig.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#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: msg32: the 32-byte message hash being verified (will not be NULL)
|
||||
* 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)
|
||||
* algo16: pointer to a 16-byte array describing the signature
|
||||
* algorithm (will not be NULL).
|
||||
* 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 *msg32,
|
||||
const unsigned char *key32,
|
||||
const unsigned char *xonly_pk32,
|
||||
const unsigned char *algo16,
|
||||
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,
|
||||
* schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
|
||||
* argument must be non-NULL, otherwise the function will fail and return 0.
|
||||
* The hash will be tagged with algo16 after removing all terminating null
|
||||
* bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
|
||||
* to "BIP0340/nonce\0\0\0"
|
||||
*/
|
||||
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* Otherwise BIP-340 compliant if the noncefp argument is NULL or
|
||||
* secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary
|
||||
* randomness.
|
||||
*
|
||||
* Returns 1 on success, 0 on failure.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
|
||||
* Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL)
|
||||
* In: msg32: the 32-byte message being signed (cannot be NULL)
|
||||
* keypair: pointer to an initialized keypair (cannot be NULL)
|
||||
* 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.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_schnorrsig_sign(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_keypair *keypair,
|
||||
secp256k1_nonce_function_hardened noncefp,
|
||||
void *ndata
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Verify a Schnorr signature.
|
||||
*
|
||||
* Returns: 1: correct signature
|
||||
* 0: incorrect signature
|
||||
* Args: ctx: a secp256k1 context object, initialized for verification.
|
||||
* In: sig64: pointer to the 64-byte signature to verify (cannot be NULL)
|
||||
* msg32: the 32-byte message being verified (cannot be NULL)
|
||||
* 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 *msg32,
|
||||
const secp256k1_xonly_pubkey *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SECP256K1_SCHNORRSIG_H */
|
||||
|
|
@ -11,6 +11,9 @@ extern "C" {
|
|||
/** Maximum number of inputs that may be given in a surjection proof */
|
||||
#define SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS 256
|
||||
|
||||
/** Maximum number of inputs that may be used in a surjection proof */
|
||||
#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 256
|
||||
|
||||
/** Number of bytes a serialized surjection proof requires given the
|
||||
* number of inputs and the number of used inputs.
|
||||
*/
|
||||
|
|
@ -19,7 +22,7 @@ extern "C" {
|
|||
|
||||
/** Maximum number of bytes a serialized surjection proof requires. */
|
||||
#define SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX \
|
||||
SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)
|
||||
SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)
|
||||
|
||||
/** Opaque data structure that holds a parsed surjection proof
|
||||
*
|
||||
|
|
@ -46,9 +49,10 @@ typedef struct {
|
|||
/** Bitmap of which input tags are used in the surjection proof */
|
||||
unsigned char used_inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8];
|
||||
/** Borromean signature: e0, scalars */
|
||||
unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS)];
|
||||
unsigned char data[32 * (1 + SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS)];
|
||||
} secp256k1_surjectionproof;
|
||||
|
||||
#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE
|
||||
/** Parse a surjection proof
|
||||
*
|
||||
* Returns: 1 when the proof could be parsed, 0 otherwise.
|
||||
|
|
@ -70,6 +74,7 @@ SECP256K1_API int secp256k1_surjectionproof_parse(
|
|||
const unsigned char *input,
|
||||
size_t inputlen
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
#endif
|
||||
|
||||
/** Serialize a surjection proof
|
||||
*
|
||||
|
|
@ -134,6 +139,7 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size(
|
|||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Surjection proof initialization function; decides on inputs to use
|
||||
* To be used to initialize stack-allocated secp256k1_surjectionproof struct
|
||||
* Returns 0: inputs could not be selected
|
||||
* n: inputs were selected after n iterations of random selection
|
||||
*
|
||||
|
|
@ -142,7 +148,8 @@ SECP256K1_API size_t secp256k1_surjectionproof_serialized_size(
|
|||
* e.g. in a coinjoin with others' inputs, an ephemeral tag can be given;
|
||||
* this won't match the output tag but might be used in the anonymity set.)
|
||||
* n_input_tags: the number of entries in the fixed_input_tags array
|
||||
* n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set
|
||||
* n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set
|
||||
* Must be <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS
|
||||
* fixed_output_tag: fixed output tag
|
||||
* max_n_iterations: the maximum number of iterations to do before giving up. Because the
|
||||
* maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is
|
||||
|
|
@ -166,6 +173,51 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_initial
|
|||
const unsigned char *random_seed32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7);
|
||||
|
||||
|
||||
/** Surjection proof allocation and initialization function; decides on inputs to use
|
||||
* Returns 0: inputs could not be selected, or malloc failure
|
||||
* n: inputs were selected after n iterations of random selection
|
||||
*
|
||||
* In: ctx: pointer to a context object
|
||||
* proof_out_p: a pointer to a pointer to `secp256k1_surjectionproof*`.
|
||||
* the newly-allocated struct pointer will be saved here.
|
||||
* fixed_input_tags: fixed input tags `A_i` for all inputs. (If the fixed tag is not known,
|
||||
* e.g. in a coinjoin with others' inputs, an ephemeral tag can be given;
|
||||
* this won't match the output tag but might be used in the anonymity set.)
|
||||
* n_input_tags: the number of entries in the fixed_input_tags array
|
||||
* n_input_tags_to_use: the number of inputs to select randomly to put in the anonymity set
|
||||
* fixed_output_tag: fixed output tag
|
||||
* max_n_iterations: the maximum number of iterations to do before giving up. Because the
|
||||
* maximum number of inputs (SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) is
|
||||
* limited to 256 the probability of giving up is smaller than
|
||||
* (255/256)^(n_input_tags_to_use*max_n_iterations).
|
||||
*
|
||||
* random_seed32: a random seed to be used for input selection
|
||||
* Out: proof_out_p: The pointer to newly-allocated proof whose bitvector will be initialized.
|
||||
* In case of failure, the pointer will be NULL.
|
||||
* input_index: The index of the actual input that is secretly mapped to the output
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_allocate_initialized(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_surjectionproof** proof_out_p,
|
||||
size_t *input_index,
|
||||
const secp256k1_fixed_asset_tag* fixed_input_tags,
|
||||
const size_t n_input_tags,
|
||||
const size_t n_input_tags_to_use,
|
||||
const secp256k1_fixed_asset_tag* fixed_output_tag,
|
||||
const size_t n_max_iterations,
|
||||
const unsigned char *random_seed32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(7);
|
||||
|
||||
/** Surjection proof destroy function
|
||||
* deallocates the struct that was allocated with secp256k1_surjectionproof_allocate_initialized
|
||||
*
|
||||
* In: proof: pointer to secp256k1_surjectionproof struct
|
||||
*/
|
||||
SECP256K1_API void secp256k1_surjectionproof_destroy(
|
||||
secp256k1_surjectionproof* proof
|
||||
) SECP256K1_ARG_NONNULL(1);
|
||||
|
||||
/** Surjection proof generation function
|
||||
* Returns 0: proof could not be created
|
||||
* 1: proof was successfully created
|
||||
|
|
@ -191,6 +243,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_surjectionproof_generat
|
|||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
|
||||
|
||||
|
||||
#ifndef USE_REDUCED_SURJECTION_PROOF_SIZE
|
||||
/** Surjection proof verification function
|
||||
* Returns 0: proof was invalid
|
||||
* 1: proof was valid
|
||||
|
|
@ -208,6 +261,7 @@ SECP256K1_API int secp256k1_surjectionproof_verify(
|
|||
size_t n_ephemeral_input_tags,
|
||||
const secp256k1_generator* ephemeral_output_tag
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ Description: Optimized C library for EC operations on curve secp256k1
|
|||
URL: https://github.com/bitcoin-core/secp256k1
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs.private: @SECP_LIBS@
|
||||
Libs: -L${libdir} -lsecp256k1
|
||||
Libs.private: @SECP_LIBS@
|
||||
|
||||
|
|
|
|||
129
sage/gen_exhaustive_groups.sage
Normal file
129
sage/gen_exhaustive_groups.sage
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# Define field size and field
|
||||
P = 2^256 - 2^32 - 977
|
||||
F = GF(P)
|
||||
BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee)
|
||||
|
||||
assert(BETA != F(1) and BETA^3 == F(1))
|
||||
|
||||
orders_done = set()
|
||||
results = {}
|
||||
first = True
|
||||
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")
|
||||
continue
|
||||
orders_done.add(n)
|
||||
# Skip curves isomorphic to the real secp256k1
|
||||
if n.is_pseudoprime():
|
||||
print(" - Isomorphic to secp256k1")
|
||||
continue
|
||||
|
||||
print("- Finding subgroups")
|
||||
|
||||
# Find what prime subgroups exist
|
||||
for f, _ in n.factor():
|
||||
print("- Analyzing subgroup of order %i" % f)
|
||||
# Skip subgroups of order >1000
|
||||
if f < 4 or f > 1000:
|
||||
print(" - Bad size")
|
||||
continue
|
||||
|
||||
# Iterate over X coordinates until we find one that is on the curve, has order f,
|
||||
# and for which curve isomorphism exists that maps it to X coordinate 1.
|
||||
for x in range(1, P):
|
||||
# Skip X coordinates not on the curve, and construct the full point otherwise.
|
||||
if not E.is_x_coord(x):
|
||||
continue
|
||||
G = E.lift_x(F(x))
|
||||
|
||||
print(" - Analyzing (multiples of) point with X=%i" % x)
|
||||
|
||||
# Skip points whose order is not a multiple of f. Project the point to have
|
||||
# order f otherwise.
|
||||
if (G.order() % f):
|
||||
print(" - Bad order")
|
||||
continue
|
||||
G = G * (G.order() // f)
|
||||
|
||||
# Find lambda for endomorphism. Skip if none can be found.
|
||||
lam = None
|
||||
for l in Integers(f)(1).nth_root(3, all=True):
|
||||
if int(l)*G == E(BETA*G[0], G[1]):
|
||||
lam = int(l)
|
||||
break
|
||||
if lam is None:
|
||||
print(" - No endomorphism for this subgroup")
|
||||
break
|
||||
|
||||
# Now look for an isomorphism of the curve that gives this point an X
|
||||
# coordinate equal to 1.
|
||||
# If (x,y) is on y^2 = x^3 + b, then (a^2*x, a^3*y) is on y^2 = x^3 + a^6*b.
|
||||
# So look for m=a^2=1/x.
|
||||
m = F(1)/G[0]
|
||||
if not m.is_square():
|
||||
print(" - No curve isomorphism maps it to a point with X=1")
|
||||
continue
|
||||
a = m.sqrt()
|
||||
rb = a^6*b
|
||||
RE = EllipticCurve(F, [0, rb])
|
||||
|
||||
# Use as generator twice the image of G under the above isormorphism.
|
||||
# This means that generator*(1/2 mod f) will have X coordinate 1.
|
||||
RG = RE(1, a^3*G[1]) * 2
|
||||
# And even Y coordinate.
|
||||
if int(RG[1]) % 2:
|
||||
RG = -RG
|
||||
assert(RG.order() == f)
|
||||
assert(lam*RG == RE(BETA*RG[0], RG[1]))
|
||||
|
||||
# We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it
|
||||
results[f] = {"b": rb, "G": RG, "lambda": lam}
|
||||
print(" - Found solution")
|
||||
break
|
||||
|
||||
print("")
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("/* To be put in src/group_impl.h: */")
|
||||
first = True
|
||||
for f in sorted(results.keys()):
|
||||
b = results[f]["b"]
|
||||
G = results[f]["G"]
|
||||
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
|
||||
first = False
|
||||
print("static const secp256k1_ge secp256k1_ge_const_g = 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(");")
|
||||
print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(")
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(");")
|
||||
print("# else")
|
||||
print("# error No known generator for the specified exhaustive test group order.")
|
||||
print("# endif")
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("/* To be put in src/scalar_impl.h: */")
|
||||
first = True
|
||||
for f in sorted(results.keys()):
|
||||
lam = results[f]["lambda"]
|
||||
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("")
|
||||
|
|
@ -65,7 +65,7 @@ class fastfrac:
|
|||
return self.top in I and self.bot not in I
|
||||
|
||||
def reduce(self,assumeZero):
|
||||
zero = self.R.ideal(map(numerator, 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):
|
||||
|
|
@ -100,7 +100,7 @@ class fastfrac:
|
|||
"""Multiply something else with a fraction."""
|
||||
return self.__mul__(other)
|
||||
|
||||
def __div__(self,other):
|
||||
def __truediv__(self,other):
|
||||
"""Divide two fractions."""
|
||||
if parent(other) == ZZ:
|
||||
return fastfrac(self.R,self.top,self.bot * other)
|
||||
|
|
@ -108,6 +108,11 @@ 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:
|
||||
|
|
@ -175,7 +180,7 @@ class constraints:
|
|||
|
||||
def conflicts(R, con):
|
||||
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
|
||||
zero = R.ideal(map(numerator, con.zero))
|
||||
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
|
||||
|
|
@ -195,7 +200,7 @@ def conflicts(R, con):
|
|||
|
||||
def get_nonzero_set(R, assume):
|
||||
"""Calculate a simple set of nonzero expressions"""
|
||||
zero = R.ideal(map(numerator, assume.zero))
|
||||
zero = R.ideal(list(map(numerator, assume.zero)))
|
||||
nonzero = set()
|
||||
for nz in map(numerator, assume.nonzero):
|
||||
for (f,n) in nz.factor():
|
||||
|
|
@ -208,7 +213,7 @@ def get_nonzero_set(R, assume):
|
|||
|
||||
def prove_nonzero(R, exprs, assume):
|
||||
"""Check whether an expression is provably nonzero, given assumptions"""
|
||||
zero = R.ideal(map(numerator, assume.zero))
|
||||
zero = R.ideal(list(map(numerator, assume.zero)))
|
||||
nonzero = get_nonzero_set(R, assume)
|
||||
expl = set()
|
||||
ok = True
|
||||
|
|
@ -250,7 +255,7 @@ def prove_zero(R, exprs, assume):
|
|||
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
|
||||
if not r:
|
||||
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
|
||||
zero = R.ideal(map(numerator, assume.zero))
|
||||
zero = R.ideal(list(map(numerator, assume.zero)))
|
||||
nonzero = prod(x for x in assume.nonzero)
|
||||
expl = []
|
||||
for expr in exprs:
|
||||
|
|
@ -265,8 +270,8 @@ 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(map(numerator, assume.zero))
|
||||
zeroextra = R.ideal(map(numerator, zerox))
|
||||
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
|
||||
|
|
|
|||
|
|
@ -175,24 +175,24 @@ laws_jacobian_weierstrass = {
|
|||
def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
|
||||
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
|
||||
F = Integers(p)
|
||||
print "Formula %s on Z%i:" % (name, p)
|
||||
print("Formula %s on Z%i:" % (name, p))
|
||||
points = []
|
||||
for x in xrange(0, p):
|
||||
for y in xrange(0, p):
|
||||
for x in range(0, p):
|
||||
for y in range(0, p):
|
||||
point = affinepoint(F(x), F(y))
|
||||
r, e = concrete_verify(on_weierstrass_curve(A, B, point))
|
||||
if r:
|
||||
points.append(point)
|
||||
|
||||
for za in xrange(1, p):
|
||||
for zb in xrange(1, p):
|
||||
for za in range(1, p):
|
||||
for zb in range(1, p):
|
||||
for pa in points:
|
||||
for pb in points:
|
||||
for ia in xrange(2):
|
||||
for ib in xrange(2):
|
||||
for ia in range(2):
|
||||
for ib in range(2):
|
||||
pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
|
||||
pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
|
||||
for branch in xrange(0, branches):
|
||||
for branch in range(0, branches):
|
||||
assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
|
||||
pC.X = F(pC.X)
|
||||
pC.Y = F(pC.Y)
|
||||
|
|
@ -206,13 +206,13 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
|
|||
r, e = concrete_verify(assumeLaw)
|
||||
if r:
|
||||
if match:
|
||||
print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
|
||||
print(" multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity))
|
||||
else:
|
||||
match = True
|
||||
r, e = concrete_verify(require)
|
||||
if not r:
|
||||
print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
|
||||
print
|
||||
print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
|
||||
print()
|
||||
|
||||
|
||||
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
|
||||
|
|
@ -242,9 +242,9 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
|
|||
for key in laws_jacobian_weierstrass:
|
||||
res[key] = []
|
||||
|
||||
print ("Formula " + name + ":")
|
||||
print("Formula " + name + ":")
|
||||
count = 0
|
||||
for branch in xrange(branches):
|
||||
for branch in range(branches):
|
||||
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
|
||||
pC.X = lift(pC.X)
|
||||
pC.Y = lift(pC.Y)
|
||||
|
|
@ -255,10 +255,10 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
|
|||
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
|
||||
|
||||
for key in res:
|
||||
print " %s:" % key
|
||||
print(" %s:" % key)
|
||||
val = res[key]
|
||||
for x in val:
|
||||
if x[0] is not None:
|
||||
print " branch %i: %s" % (x[1], x[0])
|
||||
print(" branch %i: %s" % (x[1], x[0]))
|
||||
|
||||
print
|
||||
print()
|
||||
|
|
|
|||
|
|
@ -16,15 +16,9 @@ Note:
|
|||
*/
|
||||
|
||||
.syntax unified
|
||||
.arch armv7-a
|
||||
@ eabi attributes - see readelf -A
|
||||
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
|
||||
.eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
|
||||
.eabi_attribute 10, 0 @ Tag_FP_arch = none
|
||||
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
|
||||
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
|
||||
.eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed
|
||||
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
|
||||
.text
|
||||
|
||||
@ Field constants
|
||||
|
|
|
|||
80
src/assumptions.h
Normal file
80
src/assumptions.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2020 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_ASSUMPTIONS_H
|
||||
#define SECP256K1_ASSUMPTIONS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* This library, like most software, relies on a number of compiler implementation defined (but not undefined)
|
||||
behaviours. Although the behaviours we require are essentially universal we test them specifically here to
|
||||
reduce the odds of experiencing an unwelcome surprise.
|
||||
*/
|
||||
|
||||
struct secp256k1_assumption_checker {
|
||||
/* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not
|
||||
allowed. */
|
||||
int dummy_array[(
|
||||
/* Bytes are 8 bits. */
|
||||
(CHAR_BIT == 8) &&
|
||||
|
||||
/* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32
|
||||
without signed overflow, which would be undefined behaviour. */
|
||||
(UINT_MAX <= UINT32_MAX) &&
|
||||
|
||||
/* Conversions from unsigned to signed outside of the bounds of the signed type are
|
||||
implementation-defined. Verify that they function as reinterpreting the lower
|
||||
bits of the input in two's complement notation. Do this for conversions:
|
||||
- from uint(N)_t to int(N)_t with negative result
|
||||
- from uint(2N)_t to int(N)_t with negative result
|
||||
- from int(2N)_t to int(N)_t with negative result
|
||||
- from int(2N)_t to int(N)_t with positive result */
|
||||
|
||||
/* To int8_t. */
|
||||
((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) &&
|
||||
((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) &&
|
||||
((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) &&
|
||||
((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) &&
|
||||
|
||||
/* To int16_t. */
|
||||
((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) &&
|
||||
((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) &&
|
||||
((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) &&
|
||||
((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) &&
|
||||
|
||||
/* To int32_t. */
|
||||
((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) &&
|
||||
((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) &&
|
||||
((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) &&
|
||||
((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) &&
|
||||
|
||||
/* To int64_t. */
|
||||
((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) &&
|
||||
#if defined(SECP256K1_WIDEMUL_INT128)
|
||||
((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) &&
|
||||
(((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) &&
|
||||
(((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) &&
|
||||
|
||||
/* To int128_t. */
|
||||
((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) &&
|
||||
#endif
|
||||
|
||||
/* Right shift on negative signed values is implementation defined. Verify that it
|
||||
acts as a right shift in two's complement with sign extension (i.e duplicating
|
||||
the top bit into newly added bits). */
|
||||
((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) &&
|
||||
((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) &&
|
||||
((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) &&
|
||||
((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) &&
|
||||
#if defined(SECP256K1_WIDEMUL_INT128)
|
||||
((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) &&
|
||||
#endif
|
||||
1) * 2 - 1];
|
||||
};
|
||||
|
||||
#endif /* SECP256K1_ASSUMPTIONS_H */
|
||||
|
|
@ -10,23 +10,24 @@
|
|||
#ifdef USE_BASIC_CONFIG
|
||||
|
||||
#undef USE_ASM_X86_64
|
||||
#undef USE_ENDOMORPHISM
|
||||
#undef USE_FIELD_10X26
|
||||
#undef USE_FIELD_5X52
|
||||
#undef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
#undef USE_EXTERNAL_ASM
|
||||
#undef USE_EXTERNAL_DEFAULT_CALLBACKS
|
||||
#undef USE_FIELD_INV_BUILTIN
|
||||
#undef USE_FIELD_INV_NUM
|
||||
#undef USE_NUM_GMP
|
||||
#undef USE_NUM_NONE
|
||||
#undef USE_SCALAR_4X64
|
||||
#undef USE_SCALAR_8X32
|
||||
#undef USE_SCALAR_INV_BUILTIN
|
||||
#undef USE_SCALAR_INV_NUM
|
||||
#undef USE_FORCE_WIDEMUL_INT64
|
||||
#undef USE_FORCE_WIDEMUL_INT128
|
||||
#undef ECMULT_WINDOW_SIZE
|
||||
|
||||
#define USE_NUM_NONE 1
|
||||
#define USE_FIELD_INV_BUILTIN 1
|
||||
#define USE_SCALAR_INV_BUILTIN 1
|
||||
#define USE_FIELD_10X26 1
|
||||
#define USE_SCALAR_8X32 1
|
||||
#define USE_WIDEMUL_64 1
|
||||
#define ECMULT_WINDOW_SIZE 15
|
||||
|
||||
#endif /* USE_BASIC_CONFIG */
|
||||
|
||||
|
|
|
|||
97
src/bench.h
97
src/bench.h
|
|
@ -7,45 +7,87 @@
|
|||
#ifndef SECP256K1_BENCH_H
|
||||
#define SECP256K1_BENCH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "sys/time.h"
|
||||
|
||||
static double gettimedouble(void) {
|
||||
static int64_t gettime_i64(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_usec * 0.000001 + tv.tv_sec;
|
||||
return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL;
|
||||
}
|
||||
|
||||
void print_number(double x) {
|
||||
double y = x;
|
||||
int c = 0;
|
||||
if (y < 0.0) {
|
||||
y = -y;
|
||||
#define FP_EXP (6)
|
||||
#define FP_MULT (1000000LL)
|
||||
|
||||
/* Format fixed point number. */
|
||||
void print_number(const int64_t x) {
|
||||
int64_t x_abs, y;
|
||||
int c, i, rounding;
|
||||
size_t ptr;
|
||||
char buffer[30];
|
||||
|
||||
if (x == INT64_MIN) {
|
||||
/* Prevent UB. */
|
||||
printf("ERR");
|
||||
return;
|
||||
}
|
||||
while (y > 0 && y < 100.0) {
|
||||
y *= 10.0;
|
||||
x_abs = x < 0 ? -x : x;
|
||||
|
||||
/* Determine how many decimals we want to show (more than FP_EXP makes no
|
||||
* sense). */
|
||||
y = x_abs;
|
||||
c = 0;
|
||||
while (y > 0LL && y < 100LL * FP_MULT && c < FP_EXP) {
|
||||
y *= 10LL;
|
||||
c++;
|
||||
}
|
||||
printf("%.*f", c, x);
|
||||
|
||||
/* Round to 'c' decimals. */
|
||||
y = x_abs;
|
||||
rounding = 0;
|
||||
for (i = c; i < FP_EXP; ++i) {
|
||||
rounding = (y % 10) >= 5;
|
||||
y /= 10;
|
||||
}
|
||||
y += rounding;
|
||||
|
||||
/* Format and print the number. */
|
||||
ptr = sizeof(buffer) - 1;
|
||||
buffer[ptr] = 0;
|
||||
if (c != 0) {
|
||||
for (i = 0; i < c; ++i) {
|
||||
buffer[--ptr] = '0' + (y % 10);
|
||||
y /= 10;
|
||||
}
|
||||
buffer[--ptr] = '.';
|
||||
}
|
||||
do {
|
||||
buffer[--ptr] = '0' + (y % 10);
|
||||
y /= 10;
|
||||
} while (y != 0);
|
||||
if (x < 0) {
|
||||
buffer[--ptr] = '-';
|
||||
}
|
||||
printf("%s", &buffer[ptr]);
|
||||
}
|
||||
|
||||
void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) {
|
||||
void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) {
|
||||
int i;
|
||||
double min = HUGE_VAL;
|
||||
double sum = 0.0;
|
||||
double max = 0.0;
|
||||
int64_t min = INT64_MAX;
|
||||
int64_t sum = 0;
|
||||
int64_t max = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
double begin, total;
|
||||
int64_t begin, total;
|
||||
if (setup != NULL) {
|
||||
setup(data);
|
||||
}
|
||||
begin = gettimedouble();
|
||||
benchmark(data);
|
||||
total = gettimedouble() - begin;
|
||||
begin = gettime_i64();
|
||||
benchmark(data, iter);
|
||||
total = gettime_i64() - begin;
|
||||
if (teardown != NULL) {
|
||||
teardown(data);
|
||||
teardown(data, iter);
|
||||
}
|
||||
if (total < min) {
|
||||
min = total;
|
||||
|
|
@ -56,11 +98,11 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v
|
|||
sum += total;
|
||||
}
|
||||
printf("%s: min ", name);
|
||||
print_number(min * 1000000.0 / iter);
|
||||
print_number(min * FP_MULT / iter);
|
||||
printf("us / avg ");
|
||||
print_number((sum / count) * 1000000.0 / iter);
|
||||
print_number(((sum * FP_MULT) / count) / iter);
|
||||
printf("us / max ");
|
||||
print_number(max * 1000000.0 / iter);
|
||||
print_number(max * FP_MULT / iter);
|
||||
printf("us\n");
|
||||
}
|
||||
|
||||
|
|
@ -79,4 +121,13 @@ int have_flag(int argc, char** argv, char *flag) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int get_iters(int default_iters) {
|
||||
char* env = getenv("SECP256K1_BENCH_ITERS");
|
||||
if (env) {
|
||||
return strtol(env, NULL, 0);
|
||||
} else {
|
||||
return default_iters;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_BENCH_H */
|
||||
|
|
|
|||
|
|
@ -28,20 +28,18 @@ static void bench_ecdh_setup(void* arg) {
|
|||
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
|
||||
};
|
||||
|
||||
/* create a context with no capabilities */
|
||||
data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
|
||||
for (i = 0; i < 32; i++) {
|
||||
data->scalar[i] = i + 1;
|
||||
}
|
||||
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
|
||||
}
|
||||
|
||||
static void bench_ecdh(void* arg) {
|
||||
static void bench_ecdh(void* arg, int iters) {
|
||||
int i;
|
||||
unsigned char res[32];
|
||||
bench_ecdh_data *data = (bench_ecdh_data*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +47,13 @@ static void bench_ecdh(void* arg) {
|
|||
int main(void) {
|
||||
bench_ecdh_data data;
|
||||
|
||||
run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000);
|
||||
int iters = get_iters(20000);
|
||||
|
||||
/* create a context with no capabilities */
|
||||
data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
|
||||
|
||||
run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include "secp256k1.c"
|
||||
|
||||
#define POINTS 32768
|
||||
#define ITERS 10000
|
||||
|
||||
typedef struct {
|
||||
/* Setup once in advance */
|
||||
|
|
@ -55,16 +54,16 @@ static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, vo
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void bench_ecmult(void* arg) {
|
||||
static void bench_ecmult(void* arg, int iters) {
|
||||
bench_data* data = (bench_data*)arg;
|
||||
|
||||
size_t count = data->count;
|
||||
int includes_g = data->includes_g;
|
||||
size_t iters = 1 + ITERS / count;
|
||||
size_t iter;
|
||||
int iter;
|
||||
int count = data->count;
|
||||
iters = iters / data->count;
|
||||
|
||||
for (iter = 0; iter < iters; ++iter) {
|
||||
data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
|
||||
data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
|
||||
data->offset1 = (data->offset1 + count) % POINTS;
|
||||
data->offset2 = (data->offset2 + count - 1) % POINTS;
|
||||
}
|
||||
|
|
@ -76,10 +75,10 @@ static void bench_ecmult_setup(void* arg) {
|
|||
data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
|
||||
}
|
||||
|
||||
static void bench_ecmult_teardown(void* arg) {
|
||||
static void bench_ecmult_teardown(void* arg, int iters) {
|
||||
bench_data* data = (bench_data*)arg;
|
||||
size_t iters = 1 + ITERS / data->count;
|
||||
size_t iter;
|
||||
int iter;
|
||||
iters = iters / data->count;
|
||||
/* Verify the results in teardown, to avoid doing comparisons while benchmarking. */
|
||||
for (iter = 0; iter < iters; ++iter) {
|
||||
secp256k1_gej tmp;
|
||||
|
|
@ -104,10 +103,10 @@ static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
|
|||
CHECK(!overflow);
|
||||
}
|
||||
|
||||
static void run_test(bench_data* data, size_t count, int includes_g) {
|
||||
static void run_test(bench_data* data, size_t count, int includes_g, int num_iters) {
|
||||
char str[32];
|
||||
static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
size_t iters = 1 + ITERS / count;
|
||||
size_t iters = 1 + num_iters / count;
|
||||
size_t iter;
|
||||
|
||||
data->count = count;
|
||||
|
|
@ -130,7 +129,7 @@ static void run_test(bench_data* data, size_t count, int includes_g) {
|
|||
|
||||
/* Run the benchmark. */
|
||||
sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count);
|
||||
run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * (1 + ITERS / count));
|
||||
run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * iters);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
|
@ -139,6 +138,13 @@ int main(int argc, char **argv) {
|
|||
secp256k1_gej* pubkeys_gej;
|
||||
size_t scratch_size;
|
||||
|
||||
int iters = get_iters(10000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
|
||||
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
|
||||
data.ecmult_multi = secp256k1_ecmult_multi_var;
|
||||
|
||||
if (argc > 1) {
|
||||
if(have_flag(argc, argv, "pippenger_wnaf")) {
|
||||
printf("Using pippenger_wnaf:\n");
|
||||
|
|
@ -146,20 +152,24 @@ int main(int argc, char **argv) {
|
|||
} else if(have_flag(argc, argv, "strauss_wnaf")) {
|
||||
printf("Using strauss_wnaf:\n");
|
||||
data.ecmult_multi = secp256k1_ecmult_strauss_batch_single;
|
||||
} else if(have_flag(argc, argv, "simple")) {
|
||||
printf("Using simple algorithm:\n");
|
||||
data.ecmult_multi = secp256k1_ecmult_multi_var;
|
||||
secp256k1_scratch_space_destroy(data.ctx, data.scratch);
|
||||
data.scratch = NULL;
|
||||
} else {
|
||||
fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
|
||||
fprintf(stderr, "Use 'pippenger_wnaf', 'strauss_wnaf', 'simple' or no argument to benchmark a combined algorithm.\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
data.ecmult_multi = secp256k1_ecmult_multi_var;
|
||||
}
|
||||
|
||||
/* Allocate stuff */
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
|
||||
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
|
||||
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
|
||||
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
|
||||
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
|
||||
data.expected_output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
|
||||
data.output = malloc(sizeof(secp256k1_gej) * (ITERS + 1));
|
||||
data.expected_output = malloc(sizeof(secp256k1_gej) * (iters + 1));
|
||||
data.output = malloc(sizeof(secp256k1_gej) * (iters + 1));
|
||||
|
||||
/* Generate a set of scalars, and private/public keypairs. */
|
||||
pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
|
||||
|
|
@ -176,16 +186,24 @@ int main(int argc, char **argv) {
|
|||
free(pubkeys_gej);
|
||||
|
||||
for (i = 1; i <= 8; ++i) {
|
||||
run_test(&data, i, 1);
|
||||
run_test(&data, i, 1, iters);
|
||||
}
|
||||
|
||||
for (p = 0; p <= 11; ++p) {
|
||||
for (i = 9; i <= 16; ++i) {
|
||||
run_test(&data, i << p, 1);
|
||||
/* This is disabled with low count of iterations because the loop runs 77 times even with iters=1
|
||||
* and the higher it goes the longer the computation takes(more points)
|
||||
* So we don't run this benchmark with low iterations to prevent slow down */
|
||||
if (iters > 2) {
|
||||
for (p = 0; p <= 11; ++p) {
|
||||
for (i = 9; i <= 16; ++i) {
|
||||
run_test(&data, i << p, 1, iters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.scratch != NULL) {
|
||||
secp256k1_scratch_space_destroy(data.ctx, data.scratch);
|
||||
}
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
secp256k1_scratch_space_destroy(data.scratch);
|
||||
free(data.scalars);
|
||||
free(data.pubkeys);
|
||||
free(data.seckeys);
|
||||
|
|
|
|||
|
|
@ -23,22 +23,22 @@ static void bench_generator_setup(void* arg) {
|
|||
memset(data->blind, 0x13, 32);
|
||||
}
|
||||
|
||||
static void bench_generator_generate(void* arg) {
|
||||
static void bench_generator_generate(void* arg, int iters) {
|
||||
int i;
|
||||
bench_generator_t *data = (bench_generator_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_generator gen;
|
||||
CHECK(secp256k1_generator_generate(data->ctx, &gen, data->key));
|
||||
data->key[i & 31]++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bench_generator_generate_blinded(void* arg) {
|
||||
static void bench_generator_generate_blinded(void* arg, int iters) {
|
||||
int i;
|
||||
bench_generator_t *data = (bench_generator_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_generator gen;
|
||||
CHECK(secp256k1_generator_generate_blinded(data->ctx, &gen, data->key, data->blind));
|
||||
data->key[1 + (i & 30)]++;
|
||||
|
|
@ -48,11 +48,12 @@ static void bench_generator_generate_blinded(void* arg) {
|
|||
|
||||
int main(void) {
|
||||
bench_generator_t data;
|
||||
int iters = get_iters(20000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, 20000);
|
||||
run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, 20000);
|
||||
run_benchmark("generator_generate", bench_generator_generate, bench_generator_setup, NULL, &data, 10, iters);
|
||||
run_benchmark("generator_generate_blinded", bench_generator_generate_blinded, bench_generator_setup, NULL, &data, 10, iters);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "include/secp256k1.h"
|
||||
|
||||
#include "assumptions.h"
|
||||
#include "util.h"
|
||||
#include "hash_impl.h"
|
||||
#include "num_impl.h"
|
||||
|
|
@ -19,10 +20,10 @@
|
|||
#include "secp256k1.c"
|
||||
|
||||
typedef struct {
|
||||
secp256k1_scalar scalar_x, scalar_y;
|
||||
secp256k1_fe fe_x, fe_y;
|
||||
secp256k1_ge ge_x, ge_y;
|
||||
secp256k1_gej gej_x, gej_y;
|
||||
secp256k1_scalar scalar[2];
|
||||
secp256k1_fe fe[4];
|
||||
secp256k1_ge ge[2];
|
||||
secp256k1_gej gej[2];
|
||||
unsigned char data[64];
|
||||
int wnaf[256];
|
||||
} bench_inv;
|
||||
|
|
@ -30,338 +31,401 @@ typedef struct {
|
|||
void bench_setup(void* arg) {
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
static const unsigned char init_x[32] = {
|
||||
0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
|
||||
0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
|
||||
0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
|
||||
0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
|
||||
static const unsigned char init[4][32] = {
|
||||
/* Initializer for scalar[0], fe[0], first half of data, the X coordinate of ge[0],
|
||||
and the (implied affine) X coordinate of gej[0]. */
|
||||
{
|
||||
0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
|
||||
0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
|
||||
0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
|
||||
0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
|
||||
},
|
||||
/* Initializer for scalar[1], fe[1], first half of data, the X coordinate of ge[1],
|
||||
and the (implied affine) X coordinate of gej[1]. */
|
||||
{
|
||||
0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
|
||||
0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
|
||||
0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
|
||||
0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
|
||||
},
|
||||
/* Initializer for fe[2] and the Z coordinate of gej[0]. */
|
||||
{
|
||||
0x3d, 0x2d, 0xef, 0xf4, 0x25, 0x98, 0x4f, 0x5d,
|
||||
0xe2, 0xca, 0x5f, 0x41, 0x3f, 0x3f, 0xce, 0x44,
|
||||
0xaa, 0x2c, 0x53, 0x8a, 0xc6, 0x59, 0x1f, 0x38,
|
||||
0x38, 0x23, 0xe4, 0x11, 0x27, 0xc6, 0xa0, 0xe7
|
||||
},
|
||||
/* Initializer for fe[3] and the Z coordinate of gej[1]. */
|
||||
{
|
||||
0xbd, 0x21, 0xa5, 0xe1, 0x13, 0x50, 0x73, 0x2e,
|
||||
0x52, 0x98, 0xc8, 0x9e, 0xab, 0x00, 0xa2, 0x68,
|
||||
0x43, 0xf5, 0xd7, 0x49, 0x80, 0x72, 0xa7, 0xf3,
|
||||
0xd7, 0x60, 0xe6, 0xab, 0x90, 0x92, 0xdf, 0xc5
|
||||
}
|
||||
};
|
||||
|
||||
static const unsigned char init_y[32] = {
|
||||
0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
|
||||
0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
|
||||
0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
|
||||
0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
|
||||
};
|
||||
|
||||
secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL);
|
||||
secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL);
|
||||
secp256k1_fe_set_b32(&data->fe_x, init_x);
|
||||
secp256k1_fe_set_b32(&data->fe_y, init_y);
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0));
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1));
|
||||
secp256k1_gej_set_ge(&data->gej_x, &data->ge_x);
|
||||
secp256k1_gej_set_ge(&data->gej_y, &data->ge_y);
|
||||
memcpy(data->data, init_x, 32);
|
||||
memcpy(data->data + 32, init_y, 32);
|
||||
secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL);
|
||||
secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL);
|
||||
secp256k1_fe_set_b32(&data->fe[0], init[0]);
|
||||
secp256k1_fe_set_b32(&data->fe[1], init[1]);
|
||||
secp256k1_fe_set_b32(&data->fe[2], init[2]);
|
||||
secp256k1_fe_set_b32(&data->fe[3], init[3]);
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge[0], &data->fe[0], 0));
|
||||
CHECK(secp256k1_ge_set_xo_var(&data->ge[1], &data->fe[1], 1));
|
||||
secp256k1_gej_set_ge(&data->gej[0], &data->ge[0]);
|
||||
secp256k1_gej_rescale(&data->gej[0], &data->fe[2]);
|
||||
secp256k1_gej_set_ge(&data->gej[1], &data->ge[1]);
|
||||
secp256k1_gej_rescale(&data->gej[1], &data->fe[3]);
|
||||
memcpy(data->data, init[0], 32);
|
||||
memcpy(data->data + 32, init[1], 32);
|
||||
}
|
||||
|
||||
void bench_scalar_add(void* arg) {
|
||||
void bench_scalar_add(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_scalar_negate(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_negate(&data->scalar[0], &data->scalar[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_negate(void* arg) {
|
||||
void bench_scalar_sqr(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_sqr(void* arg) {
|
||||
void bench_scalar_mul(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_mul(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_mul(void* arg) {
|
||||
void bench_scalar_split(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_split_lambda(&data->scalar[0], &data->scalar[1], &data->scalar[0]);
|
||||
j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_scalar_inverse(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_inverse(&data->scalar[0], &data->scalar[0]);
|
||||
j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_scalar_inverse_var(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_scalar_inverse_var(&data->scalar[0], &data->scalar[0]);
|
||||
j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_field_normalize(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_normalize(&data->fe[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
void bench_scalar_split(void* arg) {
|
||||
void bench_field_normalize_weak(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_scalar l, r;
|
||||
secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_normalize_weak(&data->fe[0]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void bench_scalar_inverse(void* arg) {
|
||||
void bench_field_mul(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000; i++) {
|
||||
secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_mul(&data->fe[0], &data->fe[0], &data->fe[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_scalar_inverse_var(void* arg) {
|
||||
void bench_field_sqr(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000; i++) {
|
||||
secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_sqr(&data->fe[0], &data->fe[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_normalize(void* arg) {
|
||||
void bench_field_inverse(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_fe_normalize(&data->fe_x);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_inv(&data->fe[0], &data->fe[0]);
|
||||
secp256k1_fe_add(&data->fe[0], &data->fe[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_normalize_weak(void* arg) {
|
||||
void bench_field_inverse_var(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 2000000; i++) {
|
||||
secp256k1_fe_normalize_weak(&data->fe_x);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_fe_inv_var(&data->fe[0], &data->fe[0]);
|
||||
secp256k1_fe_add(&data->fe[0], &data->fe[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_mul(void* arg) {
|
||||
void bench_field_sqrt(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
secp256k1_fe t;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
t = data->fe[0];
|
||||
j += secp256k1_fe_sqrt(&data->fe[0], &t);
|
||||
secp256k1_fe_add(&data->fe[0], &data->fe[1]);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_group_double_var(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_gej_double_var(&data->gej[0], &data->gej[0], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_sqr(void* arg) {
|
||||
void bench_group_add_var(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_gej_add_var(&data->gej[0], &data->gej[0], &data->gej[1], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_inverse(void* arg) {
|
||||
void bench_group_add_affine(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_inv(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_gej_add_ge(&data->gej[0], &data->gej[0], &data->ge[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_inverse_var(void* arg) {
|
||||
void bench_group_add_affine_var(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_gej_add_ge_var(&data->gej[0], &data->gej[0], &data->ge[1], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_field_sqrt(void* arg) {
|
||||
void bench_group_jacobi_var(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
j += secp256k1_gej_has_quad_y_var(&data->gej[0]);
|
||||
/* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to
|
||||
secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will
|
||||
generally not correspond to a point on the curve, but this is not a problem
|
||||
for the code being benchmarked here. Adding and normalizing have less
|
||||
overhead than EC operations (which could guarantee the point remains on the
|
||||
curve). */
|
||||
secp256k1_fe_add(&data->gej[0].y, &data->fe[1]);
|
||||
secp256k1_fe_add(&data->gej[0].z, &data->fe[2]);
|
||||
secp256k1_fe_normalize_var(&data->gej[0].y);
|
||||
secp256k1_fe_normalize_var(&data->gej[0].z);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
|
||||
void bench_group_to_affine_var(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
for (i = 0; i < iters; ++i) {
|
||||
secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]);
|
||||
/* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates.
|
||||
Similar to bench_group_jacobi_var, this approach does not result in
|
||||
coordinates of points on the curve. */
|
||||
secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y);
|
||||
secp256k1_fe_add(&data->gej[0].y, &data->fe[2]);
|
||||
secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x);
|
||||
secp256k1_fe_normalize_var(&data->gej[0].x);
|
||||
secp256k1_fe_normalize_var(&data->gej[0].y);
|
||||
secp256k1_fe_normalize_var(&data->gej[0].z);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_double_var(void* arg) {
|
||||
int i;
|
||||
void bench_ecmult_wnaf(void* arg, int iters) {
|
||||
int i, bits = 0, overflow = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
|
||||
for (i = 0; i < iters; i++) {
|
||||
bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar[0], WINDOW_A);
|
||||
overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(overflow >= 0);
|
||||
CHECK(bits <= 256*iters);
|
||||
}
|
||||
|
||||
void bench_group_add_var(void* arg) {
|
||||
int i;
|
||||
void bench_wnaf_const(void* arg, int iters) {
|
||||
int i, bits = 0, overflow = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_add_affine(void* arg) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_add_affine_var(void* arg) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_group_jacobi_var(void* arg) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_gej_has_quad_y_var(&data->gej_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_ecmult_wnaf(void* arg) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_wnaf_const(void* arg) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256);
|
||||
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
|
||||
for (i = 0; i < iters; i++) {
|
||||
bits += secp256k1_wnaf_const(data->wnaf, &data->scalar[0], WINDOW_A, 256);
|
||||
overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
|
||||
}
|
||||
CHECK(overflow >= 0);
|
||||
CHECK(bits <= 256*iters);
|
||||
}
|
||||
|
||||
|
||||
void bench_sha256(void* arg) {
|
||||
void bench_sha256(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, data->data, 32);
|
||||
secp256k1_sha256_finalize(&sha, data->data);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_hmac_sha256(void* arg) {
|
||||
void bench_hmac_sha256(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
secp256k1_hmac_sha256 hmac;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
|
||||
secp256k1_hmac_sha256_write(&hmac, data->data, 32);
|
||||
secp256k1_hmac_sha256_finalize(&hmac, data->data);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_rfc6979_hmac_sha256(void* arg) {
|
||||
void bench_rfc6979_hmac_sha256(void* arg, int iters) {
|
||||
int i;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
secp256k1_rfc6979_hmac_sha256 rng;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_context_verify(void* arg) {
|
||||
void bench_context_verify(void* arg, int iters) {
|
||||
int i;
|
||||
(void)arg;
|
||||
for (i = 0; i < 20; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY));
|
||||
}
|
||||
}
|
||||
|
||||
void bench_context_sign(void* arg) {
|
||||
void bench_context_sign(void* arg, int iters) {
|
||||
int i;
|
||||
(void)arg;
|
||||
for (i = 0; i < 200; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN));
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef USE_NUM_NONE
|
||||
void bench_num_jacobi(void* arg) {
|
||||
int i;
|
||||
void bench_num_jacobi(void* arg, int iters) {
|
||||
int i, j = 0;
|
||||
bench_inv *data = (bench_inv*)arg;
|
||||
secp256k1_num nx, norder;
|
||||
secp256k1_num nx, na, norder;
|
||||
|
||||
secp256k1_scalar_get_num(&nx, &data->scalar_x);
|
||||
secp256k1_scalar_get_num(&nx, &data->scalar[0]);
|
||||
secp256k1_scalar_order_get_num(&norder);
|
||||
secp256k1_scalar_get_num(&norder, &data->scalar_y);
|
||||
secp256k1_scalar_get_num(&na, &data->scalar[1]);
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_num_jacobi(&nx, &norder);
|
||||
for (i = 0; i < iters; i++) {
|
||||
j += secp256k1_num_jacobi(&nx, &norder);
|
||||
secp256k1_num_add(&nx, &nx, &na);
|
||||
}
|
||||
CHECK(j <= iters);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bench_inv data;
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000);
|
||||
#endif
|
||||
int iters = get_iters(20000);
|
||||
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000);
|
||||
if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000);
|
||||
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters);
|
||||
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters);
|
||||
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters);
|
||||
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, iters);
|
||||
if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, iters);
|
||||
|
||||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
|
||||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
|
||||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 1 + iters/1000);
|
||||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 1 + iters/100);
|
||||
|
||||
#ifndef USE_NUM_NONE
|
||||
if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, iters*10);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ static void bench_rangeproof_setup(void* arg) {
|
|||
CHECK(secp256k1_rangeproof_verify(data->ctx, &minv, &maxv, &data->commit, data->proof, data->len, NULL, 0, secp256k1_generator_h));
|
||||
}
|
||||
|
||||
static void bench_rangeproof(void* arg) {
|
||||
static void bench_rangeproof(void* arg, int iters) {
|
||||
int i;
|
||||
bench_rangeproof_t *data = (bench_rangeproof_t*)arg;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
for (i = 0; i < iters/data->min_bits; i++) {
|
||||
int j;
|
||||
uint64_t minv;
|
||||
uint64_t maxv;
|
||||
|
|
@ -51,12 +51,14 @@ static void bench_rangeproof(void* arg) {
|
|||
|
||||
int main(void) {
|
||||
bench_rangeproof_t data;
|
||||
int iters;
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
data.min_bits = 32;
|
||||
iters = data.min_bits*get_iters(32);
|
||||
|
||||
run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, 1000 * data.min_bits);
|
||||
run_benchmark("rangeproof_verify_bit", bench_rangeproof, bench_rangeproof_setup, NULL, &data, 10, iters);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ typedef struct {
|
|||
unsigned char sig[64];
|
||||
} bench_recover_data;
|
||||
|
||||
void bench_recover(void* arg) {
|
||||
void bench_recover(void* arg, int iters) {
|
||||
int i;
|
||||
bench_recover_data *data = (bench_recover_data*)arg;
|
||||
secp256k1_pubkey pubkey;
|
||||
unsigned char pubkeyc[33];
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
int j;
|
||||
size_t pubkeylen = 33;
|
||||
secp256k1_ecdsa_recoverable_signature sig;
|
||||
|
|
@ -51,9 +51,11 @@ void bench_recover_setup(void* arg) {
|
|||
int main(void) {
|
||||
bench_recover_data data;
|
||||
|
||||
int iters = get_iters(20000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000);
|
||||
run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, iters);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
|
|
|
|||
102
src/bench_schnorrsig.c
Normal file
102
src/bench_schnorrsig.c
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_schnorrsig.h"
|
||||
#include "util.h"
|
||||
#include "bench.h"
|
||||
|
||||
typedef struct {
|
||||
secp256k1_context *ctx;
|
||||
int n;
|
||||
|
||||
const secp256k1_keypair **keypairs;
|
||||
const unsigned char **pk;
|
||||
const unsigned char **sigs;
|
||||
const unsigned char **msgs;
|
||||
} bench_schnorrsig_data;
|
||||
|
||||
void bench_schnorrsig_sign(void* arg, int iters) {
|
||||
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
|
||||
int i;
|
||||
unsigned char msg[32] = "benchmarkexamplemessagetemplate";
|
||||
unsigned char sig[64];
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
msg[0] = i;
|
||||
msg[1] = i >> 8;
|
||||
CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
void bench_schnorrsig_verify(void* arg, int iters) {
|
||||
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_xonly_pubkey pk;
|
||||
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int i;
|
||||
bench_schnorrsig_data data;
|
||||
int iters = get_iters(10000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *));
|
||||
data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
|
||||
data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
|
||||
data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
unsigned char sk[32];
|
||||
unsigned char *msg = (unsigned char *)malloc(32);
|
||||
unsigned char *sig = (unsigned char *)malloc(64);
|
||||
secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
|
||||
unsigned char *pk_char = (unsigned char *)malloc(32);
|
||||
secp256k1_xonly_pubkey pk;
|
||||
msg[0] = sk[0] = i;
|
||||
msg[1] = sk[1] = i >> 8;
|
||||
msg[2] = sk[2] = i >> 16;
|
||||
msg[3] = sk[3] = i >> 24;
|
||||
memset(&msg[4], 'm', 28);
|
||||
memset(&sk[4], 's', 28);
|
||||
|
||||
data.keypairs[i] = keypair;
|
||||
data.pk[i] = pk_char;
|
||||
data.msgs[i] = msg;
|
||||
data.sigs[i] = sig;
|
||||
|
||||
CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
|
||||
CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL));
|
||||
CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
|
||||
}
|
||||
|
||||
run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters);
|
||||
run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters);
|
||||
|
||||
for (i = 0; i < iters; i++) {
|
||||
free((void *)data.keypairs[i]);
|
||||
free((void *)data.pk[i]);
|
||||
free((void *)data.msgs[i]);
|
||||
free((void *)data.sigs[i]);
|
||||
}
|
||||
free(data.keypairs);
|
||||
free(data.pk);
|
||||
free(data.msgs);
|
||||
free(data.sigs);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -12,11 +12,11 @@ typedef struct {
|
|||
secp256k1_context* ctx;
|
||||
unsigned char msg[32];
|
||||
unsigned char key[32];
|
||||
} bench_sign;
|
||||
} bench_sign_data;
|
||||
|
||||
static void bench_sign_setup(void* arg) {
|
||||
int i;
|
||||
bench_sign *data = (bench_sign*)arg;
|
||||
bench_sign_data *data = (bench_sign_data*)arg;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
data->msg[i] = i + 1;
|
||||
|
|
@ -26,12 +26,12 @@ static void bench_sign_setup(void* arg) {
|
|||
}
|
||||
}
|
||||
|
||||
static void bench_sign_run(void* arg) {
|
||||
static void bench_sign_run(void* arg, int iters) {
|
||||
int i;
|
||||
bench_sign *data = (bench_sign*)arg;
|
||||
bench_sign_data *data = (bench_sign_data*)arg;
|
||||
|
||||
unsigned char sig[74];
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
size_t siglen = 74;
|
||||
int j;
|
||||
secp256k1_ecdsa_signature signature;
|
||||
|
|
@ -45,11 +45,13 @@ static void bench_sign_run(void* arg) {
|
|||
}
|
||||
|
||||
int main(void) {
|
||||
bench_sign data;
|
||||
bench_sign_data data;
|
||||
|
||||
int iters = get_iters(20000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
|
||||
run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000);
|
||||
run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters);
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <openssl/obj_mac.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
secp256k1_context *ctx;
|
||||
unsigned char msg[32];
|
||||
|
|
@ -28,13 +29,13 @@ typedef struct {
|
|||
#ifdef ENABLE_OPENSSL_TESTS
|
||||
EC_GROUP* ec_group;
|
||||
#endif
|
||||
} benchmark_verify_t;
|
||||
} bench_verify_data;
|
||||
|
||||
static void benchmark_verify(void* arg) {
|
||||
static void bench_verify(void* arg, int iters) {
|
||||
int i;
|
||||
benchmark_verify_t* data = (benchmark_verify_t*)arg;
|
||||
bench_verify_data* data = (bench_verify_data*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
data->sig[data->siglen - 1] ^= (i & 0xFF);
|
||||
|
|
@ -50,11 +51,11 @@ static void benchmark_verify(void* arg) {
|
|||
}
|
||||
|
||||
#ifdef ENABLE_OPENSSL_TESTS
|
||||
static void benchmark_verify_openssl(void* arg) {
|
||||
static void bench_verify_openssl(void* arg, int iters) {
|
||||
int i;
|
||||
benchmark_verify_t* data = (benchmark_verify_t*)arg;
|
||||
bench_verify_data* data = (bench_verify_data*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
for (i = 0; i < iters; i++) {
|
||||
data->sig[data->siglen - 1] ^= (i & 0xFF);
|
||||
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
|
||||
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
|
||||
|
|
@ -83,7 +84,9 @@ int main(void) {
|
|||
int i;
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
benchmark_verify_t data;
|
||||
bench_verify_data data;
|
||||
|
||||
int iters = get_iters(20000);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
|
|
@ -100,10 +103,10 @@ int main(void) {
|
|||
data.pubkeylen = 33;
|
||||
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
|
||||
|
||||
run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
|
||||
run_benchmark("ecdsa_verify", bench_verify, NULL, NULL, &data, 10, iters);
|
||||
#ifdef ENABLE_OPENSSL_TESTS
|
||||
data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
|
||||
run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000);
|
||||
run_benchmark("ecdsa_verify_openssl", bench_verify_openssl, NULL, NULL, &data, 10, iters);
|
||||
EC_GROUP_free(data.ec_group);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
#include "include/secp256k1.h"
|
||||
|
||||
#include "include/secp256k1_whitelist.h"
|
||||
#include "bench.h"
|
||||
#include "util.h"
|
||||
#include "bench.h"
|
||||
#include "hash_impl.h"
|
||||
#include "num_impl.h"
|
||||
#include "scalar_impl.h"
|
||||
|
|
@ -29,9 +29,12 @@ typedef struct {
|
|||
size_t n_keys;
|
||||
} bench_data;
|
||||
|
||||
static void bench_whitelist(void* arg) {
|
||||
static void bench_whitelist(void* arg, int iters) {
|
||||
bench_data* data = (bench_data*)arg;
|
||||
CHECK(secp256k1_whitelist_verify(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey) == 1);
|
||||
int i;
|
||||
for (i = 0; i < iters; i++) {
|
||||
CHECK(secp256k1_whitelist_verify(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void bench_whitelist_setup(void* arg) {
|
||||
|
|
@ -40,17 +43,17 @@ static void bench_whitelist_setup(void* arg) {
|
|||
CHECK(secp256k1_whitelist_sign(data->ctx, &data->sig, data->online_pubkeys, data->offline_pubkeys, data->n_keys, &data->sub_pubkey, data->online_seckey[i], data->summed_seckey[i], i, NULL, NULL));
|
||||
}
|
||||
|
||||
static void run_test(bench_data* data) {
|
||||
static void run_test(bench_data* data, int iters) {
|
||||
char str[32];
|
||||
sprintf(str, "whitelist_%i", (int)data->n_keys);
|
||||
run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, 1);
|
||||
run_benchmark(str, bench_whitelist, bench_whitelist_setup, NULL, data, 100, iters);
|
||||
}
|
||||
|
||||
void random_scalar_order(secp256k1_scalar *num) {
|
||||
do {
|
||||
unsigned char b32[32];
|
||||
int overflow = 0;
|
||||
secp256k1_rand256(b32);
|
||||
secp256k1_testrand256(b32);
|
||||
secp256k1_scalar_set_b32(num, b32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(num)) {
|
||||
continue;
|
||||
|
|
@ -64,6 +67,7 @@ int main(void) {
|
|||
size_t i;
|
||||
size_t n_keys = 30;
|
||||
secp256k1_scalar ssub;
|
||||
int iters = get_iters(5);
|
||||
|
||||
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
|
|
@ -96,7 +100,7 @@ int main(void) {
|
|||
/* Run test */
|
||||
for (i = 1; i <= n_keys; ++i) {
|
||||
data.n_keys = i;
|
||||
run_test(&data);
|
||||
run_test(&data, iters);
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(data.ctx);
|
||||
|
|
|
|||
|
|
@ -46,68 +46,73 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON
|
|||
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
|
||||
);
|
||||
|
||||
static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) {
|
||||
int lenleft, b1;
|
||||
size_t ret = 0;
|
||||
static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) {
|
||||
size_t lenleft;
|
||||
unsigned char b1;
|
||||
VERIFY_CHECK(len != NULL);
|
||||
*len = 0;
|
||||
if (*sigp >= sigend) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
b1 = *((*sigp)++);
|
||||
if (b1 == 0xFF) {
|
||||
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if ((b1 & 0x80) == 0) {
|
||||
/* X.690-0207 8.1.3.4 short form length octets */
|
||||
return b1;
|
||||
*len = b1;
|
||||
return 1;
|
||||
}
|
||||
if (b1 == 0x80) {
|
||||
/* Indefinite length is not allowed in DER. */
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
/* X.690-207 8.1.3.5 long form length octets */
|
||||
lenleft = b1 & 0x7F;
|
||||
if (lenleft > sigend - *sigp) {
|
||||
return -1;
|
||||
lenleft = b1 & 0x7F; /* lenleft is at least 1 */
|
||||
if (lenleft > (size_t)(sigend - *sigp)) {
|
||||
return 0;
|
||||
}
|
||||
if (**sigp == 0) {
|
||||
/* Not the shortest possible length encoding. */
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if ((size_t)lenleft > sizeof(size_t)) {
|
||||
if (lenleft > sizeof(size_t)) {
|
||||
/* The resulting length would exceed the range of a size_t, so
|
||||
* certainly longer than the passed array size.
|
||||
*/
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
while (lenleft > 0) {
|
||||
ret = (ret << 8) | **sigp;
|
||||
if (ret + lenleft > (size_t)(sigend - *sigp)) {
|
||||
/* Result exceeds the length of the passed array. */
|
||||
return -1;
|
||||
}
|
||||
*len = (*len << 8) | **sigp;
|
||||
(*sigp)++;
|
||||
lenleft--;
|
||||
}
|
||||
if (ret < 128) {
|
||||
/* Not the shortest possible length encoding. */
|
||||
return -1;
|
||||
if (*len > (size_t)(sigend - *sigp)) {
|
||||
/* Result exceeds the length of the passed array. */
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
if (*len < 128) {
|
||||
/* Not the shortest possible length encoding. */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
|
||||
int overflow = 0;
|
||||
unsigned char ra[32] = {0};
|
||||
int rlen;
|
||||
size_t rlen;
|
||||
|
||||
if (*sig == sigend || **sig != 0x02) {
|
||||
/* Not a primitive integer (X.690-0207 8.3.1). */
|
||||
return 0;
|
||||
}
|
||||
(*sig)++;
|
||||
rlen = secp256k1_der_read_len(sig, sigend);
|
||||
if (rlen <= 0 || (*sig) + rlen > sigend) {
|
||||
if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (rlen == 0 || *sig + rlen > sigend) {
|
||||
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -123,8 +128,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
|
|||
/* Negative. */
|
||||
overflow = 1;
|
||||
}
|
||||
while (rlen > 0 && **sig == 0) {
|
||||
/* Skip leading zero bytes */
|
||||
/* There is at most one leading zero byte:
|
||||
* if there were two leading zero bytes, we would have failed and returned 0
|
||||
* because of excessive 0x00 padding already. */
|
||||
if (rlen > 0 && **sig == 0) {
|
||||
/* Skip leading zero byte */
|
||||
rlen--;
|
||||
(*sig)++;
|
||||
}
|
||||
|
|
@ -144,18 +152,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
|
|||
|
||||
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
|
||||
const unsigned char *sigend = sig + size;
|
||||
int rlen;
|
||||
size_t rlen;
|
||||
if (sig == sigend || *(sig++) != 0x30) {
|
||||
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
|
||||
return 0;
|
||||
}
|
||||
rlen = secp256k1_der_read_len(&sig, sigend);
|
||||
if (rlen < 0 || sig + rlen > sigend) {
|
||||
/* Tuple exceeds bounds */
|
||||
if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (sig + rlen != sigend) {
|
||||
/* Garbage after tuple. */
|
||||
if (rlen != (size_t)(sigend - sig)) {
|
||||
/* Tuple exceeds bounds or garage after tuple. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -274,6 +280,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
|
|||
secp256k1_ge r;
|
||||
secp256k1_scalar n;
|
||||
int overflow = 0;
|
||||
int high;
|
||||
|
||||
secp256k1_ecmult_gen(ctx, &rp, nonce);
|
||||
secp256k1_ge_set_gej(&r, &rp);
|
||||
|
|
@ -281,15 +288,11 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
|
|||
secp256k1_fe_normalize(&r.y);
|
||||
secp256k1_fe_get_b32(b, &r.x);
|
||||
secp256k1_scalar_set_b32(sigr, b, &overflow);
|
||||
/* These two conditions should be checked before calling */
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr));
|
||||
VERIFY_CHECK(overflow == 0);
|
||||
|
||||
if (recid) {
|
||||
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
|
||||
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
|
||||
*/
|
||||
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
|
||||
*recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y);
|
||||
}
|
||||
secp256k1_scalar_mul(&n, sigr, seckey);
|
||||
secp256k1_scalar_add(&n, &n, message);
|
||||
|
|
@ -298,16 +301,15 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
|
|||
secp256k1_scalar_clear(&n);
|
||||
secp256k1_gej_clear(&rp);
|
||||
secp256k1_ge_clear(&r);
|
||||
if (secp256k1_scalar_is_zero(sigs)) {
|
||||
return 0;
|
||||
high = secp256k1_scalar_is_high(sigs);
|
||||
secp256k1_scalar_cond_negate(sigs, high);
|
||||
if (recid) {
|
||||
*recid ^= high;
|
||||
}
|
||||
if (secp256k1_scalar_is_high(sigs)) {
|
||||
secp256k1_scalar_negate(sigs, sigs);
|
||||
if (recid) {
|
||||
*recid ^= 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
/* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature.
|
||||
* This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N.
|
||||
*/
|
||||
return !secp256k1_scalar_is_zero(sigr) & !secp256k1_scalar_is_zero(sigs);
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_ECDSA_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
|
|||
if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) {
|
||||
secp256k1_fe x;
|
||||
return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD);
|
||||
} else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) {
|
||||
} else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
|
||||
secp256k1_fe x, y;
|
||||
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
|
||||
return 0;
|
||||
|
|
@ -54,10 +54,7 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
|
|||
|
||||
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
|
||||
secp256k1_scalar_add(key, key, tweak);
|
||||
if (secp256k1_scalar_is_zero(key)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
return !secp256k1_scalar_is_zero(key);
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
|
||||
|
|
@ -75,12 +72,11 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx,
|
|||
}
|
||||
|
||||
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
|
||||
if (secp256k1_scalar_is_zero(tweak)) {
|
||||
return 0;
|
||||
}
|
||||
int ret;
|
||||
ret = !secp256k1_scalar_is_zero(tweak);
|
||||
|
||||
secp256k1_scalar_mul(key, key, tweak);
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {
|
||||
|
|
|
|||
13
src/ecmult.h
13
src/ecmult.h
|
|
@ -15,15 +15,13 @@
|
|||
typedef struct {
|
||||
/* For accelerating the computation of a*P + b*G: */
|
||||
secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */
|
||||
#endif
|
||||
} secp256k1_ecmult_context;
|
||||
|
||||
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
|
||||
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx);
|
||||
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb);
|
||||
static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
|
||||
const secp256k1_ecmult_context *src, const secp256k1_callback *cb);
|
||||
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc);
|
||||
static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src);
|
||||
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx);
|
||||
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx);
|
||||
|
||||
|
|
@ -37,11 +35,12 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge
|
|||
* Chooses the right algorithm for a given number of points and scratch space
|
||||
* size. Resets and overwrites the given scratch space. If the points do not
|
||||
* fit in the scratch space the algorithm is repeatedly run with batches of
|
||||
* points.
|
||||
* points. If no scratch space is given then a simple algorithm is used that
|
||||
* simply multiplies the points with the corresponding scalars and adds them up.
|
||||
* Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
|
||||
* 0 if there is not enough scratch space for a single point or
|
||||
* callback returns 0
|
||||
*/
|
||||
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
|
||||
static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
|
||||
|
||||
#endif /* SECP256K1_ECMULT_H */
|
||||
|
|
|
|||
|
|
@ -10,8 +10,11 @@
|
|||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
|
||||
/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
|
||||
* one because we internally sometimes add 2 to the number during the WNAF conversion. */
|
||||
/**
|
||||
* Multiply: R = q*A (in constant-time)
|
||||
* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus
|
||||
* one because we internally sometimes add 2 to the number during the WNAF conversion.
|
||||
*/
|
||||
static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits);
|
||||
|
||||
#endif /* SECP256K1_ECMULT_CONST_H */
|
||||
|
|
|
|||
|
|
@ -14,16 +14,22 @@
|
|||
|
||||
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
|
||||
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
|
||||
int m; \
|
||||
int abs_n = (n) * (((n) > 0) * 2 - 1); \
|
||||
int idx_n = abs_n / 2; \
|
||||
int m = 0; \
|
||||
/* Extract the sign-bit for a constant time absolute-value. */ \
|
||||
int mask = (n) >> (sizeof(n) * CHAR_BIT - 1); \
|
||||
int abs_n = ((n) + mask) ^ mask; \
|
||||
int idx_n = abs_n >> 1; \
|
||||
secp256k1_fe neg_y; \
|
||||
VERIFY_CHECK(((n) & 1) == 1); \
|
||||
VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
|
||||
VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \
|
||||
VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \
|
||||
VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \
|
||||
for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \
|
||||
/* Unconditionally set r->x = (pre)[m].x. r->y = (pre)[m].y. because it's either the correct one \
|
||||
* or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \
|
||||
(r)->x = (pre)[m].x; \
|
||||
(r)->y = (pre)[m].y; \
|
||||
for (m = 1; m < ECMULT_TABLE_SIZE(w); m++) { \
|
||||
/* This loop is used to avoid secret data in array indices. See
|
||||
* the comment in ecmult_gen_impl.h for rationale. */ \
|
||||
secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \
|
||||
|
|
@ -44,11 +50,11 @@
|
|||
*
|
||||
* Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar
|
||||
* Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.)
|
||||
* CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003
|
||||
* CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlag Berlin Heidelberg 2003
|
||||
*
|
||||
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
|
||||
*/
|
||||
static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) {
|
||||
static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) {
|
||||
int global_sign;
|
||||
int skew = 0;
|
||||
int word = 0;
|
||||
|
|
@ -59,8 +65,12 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
|
|||
|
||||
int flip;
|
||||
int bit;
|
||||
secp256k1_scalar neg_s;
|
||||
secp256k1_scalar s;
|
||||
int not_neg_one;
|
||||
|
||||
VERIFY_CHECK(w > 0);
|
||||
VERIFY_CHECK(size > 0);
|
||||
|
||||
/* Note that we cannot handle even numbers by negating them to be odd, as is
|
||||
* done in other implementations, since if our scalars were specified to have
|
||||
* width < 256 for performance reasons, their negations would have width 256
|
||||
|
|
@ -75,12 +85,13 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
|
|||
* {1, 2} we want to add to the scalar when ensuring that it's odd. Further
|
||||
* complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
|
||||
* we need to special-case it in this logic. */
|
||||
flip = secp256k1_scalar_is_high(&s);
|
||||
flip = secp256k1_scalar_is_high(scalar);
|
||||
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
|
||||
bit = flip ^ !secp256k1_scalar_is_even(&s);
|
||||
bit = flip ^ !secp256k1_scalar_is_even(scalar);
|
||||
/* We check for negative one, since adding 2 to it will cause an overflow */
|
||||
secp256k1_scalar_negate(&neg_s, &s);
|
||||
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
|
||||
secp256k1_scalar_negate(&s, scalar);
|
||||
not_neg_one = !secp256k1_scalar_is_one(&s);
|
||||
s = *scalar;
|
||||
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
|
||||
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
|
||||
* that we added two to it and flipped it. In fact for -1 these operations are
|
||||
|
|
@ -93,23 +104,29 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
|
|||
|
||||
/* 4 */
|
||||
u_last = secp256k1_scalar_shr_int(&s, w);
|
||||
while (word * w < size) {
|
||||
int sign;
|
||||
do {
|
||||
int even;
|
||||
|
||||
/* 4.1 4.4 */
|
||||
u = secp256k1_scalar_shr_int(&s, w);
|
||||
/* 4.2 */
|
||||
even = ((u & 1) == 0);
|
||||
sign = 2 * (u_last > 0) - 1;
|
||||
u += sign * even;
|
||||
u_last -= sign * even * (1 << w);
|
||||
/* In contrast to the original algorithm, u_last is always > 0 and
|
||||
* therefore we do not need to check its sign. In particular, it's easy
|
||||
* to see that u_last is never < 0 because u is never < 0. Moreover,
|
||||
* u_last is never = 0 because u is never even after a loop
|
||||
* iteration. The same holds analogously for the initial value of
|
||||
* u_last (in the first loop iteration). */
|
||||
VERIFY_CHECK(u_last > 0);
|
||||
VERIFY_CHECK((u_last & 1) == 1);
|
||||
u += even;
|
||||
u_last -= even * (1 << w);
|
||||
|
||||
/* 4.3, adapted for global sign change */
|
||||
wnaf[word++] = u_last * global_sign;
|
||||
|
||||
u_last = u;
|
||||
}
|
||||
} while (word * w < size);
|
||||
wnaf[word] = u * global_sign;
|
||||
|
||||
VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
|
||||
|
|
@ -123,33 +140,26 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
secp256k1_fe Z;
|
||||
|
||||
int skew_1;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
int skew_lam;
|
||||
secp256k1_scalar q_1, q_lam;
|
||||
#endif
|
||||
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
|
||||
int i;
|
||||
secp256k1_scalar sc = *scalar;
|
||||
|
||||
/* build wnaf representation for q. */
|
||||
int rsize = size;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
rsize = 128;
|
||||
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
|
||||
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128);
|
||||
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128);
|
||||
secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar);
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128);
|
||||
skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size);
|
||||
skew_lam = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Calculate odd multiples of a.
|
||||
|
|
@ -163,13 +173,12 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
|
||||
secp256k1_fe_normalize_weak(&pre_a[i].y);
|
||||
}
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
|
||||
secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* first loop iteration (separated out so we can directly set r, rather
|
||||
* than having it start at infinity, get doubled several times, then have
|
||||
|
|
@ -178,34 +187,30 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
VERIFY_CHECK(i != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
|
||||
secp256k1_gej_set_ge(r, &tmpa);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)];
|
||||
VERIFY_CHECK(i != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
}
|
||||
#endif
|
||||
/* remaining loop iterations */
|
||||
for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) {
|
||||
int n;
|
||||
int j;
|
||||
for (j = 0; j < WINDOW_A - 1; ++j) {
|
||||
secp256k1_gej_double_nonzero(r, r, NULL);
|
||||
secp256k1_gej_double(r, r);
|
||||
}
|
||||
|
||||
n = wnaf_1[i];
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
|
||||
VERIFY_CHECK(n != 0);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
n = wnaf_lam[i];
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
|
||||
VERIFY_CHECK(n != 0);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
secp256k1_fe_mul(&r->z, &r->z, &Z);
|
||||
|
|
@ -214,43 +219,35 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
/* Correct for wNAF skew */
|
||||
secp256k1_ge correction = *a;
|
||||
secp256k1_ge_storage correction_1_stor;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_storage correction_lam_stor;
|
||||
#endif
|
||||
secp256k1_ge_storage a2_stor;
|
||||
secp256k1_gej tmpj;
|
||||
secp256k1_gej_set_ge(&tmpj, &correction);
|
||||
secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
|
||||
secp256k1_ge_set_gej(&correction, &tmpj);
|
||||
secp256k1_ge_to_storage(&correction_1_stor, a);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
secp256k1_ge_to_storage(&correction_lam_stor, a);
|
||||
}
|
||||
#endif
|
||||
secp256k1_ge_to_storage(&a2_stor, &correction);
|
||||
|
||||
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
|
||||
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Apply the correction */
|
||||
secp256k1_ge_from_storage(&correction, &correction_1_stor);
|
||||
secp256k1_ge_neg(&correction, &correction);
|
||||
secp256k1_gej_add_ge(r, r, &correction);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (size > 128) {
|
||||
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
|
||||
secp256k1_ge_neg(&correction, &correction);
|
||||
secp256k1_ge_mul_lambda(&correction, &correction);
|
||||
secp256k1_gej_add_ge(r, r, &correction);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,28 +10,35 @@
|
|||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
|
||||
#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8
|
||||
# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8."
|
||||
#endif
|
||||
#define ECMULT_GEN_PREC_B ECMULT_GEN_PREC_BITS
|
||||
#define ECMULT_GEN_PREC_G (1 << ECMULT_GEN_PREC_B)
|
||||
#define ECMULT_GEN_PREC_N (256 / ECMULT_GEN_PREC_B)
|
||||
|
||||
typedef struct {
|
||||
/* For accelerating the computation of a*G:
|
||||
* To harden against timing attacks, use the following mechanism:
|
||||
* * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63.
|
||||
* * Compute sum(n_i * 16^i * G + U_i, i=0..63), where:
|
||||
* * U_i = U * 2^i (for i=0..62)
|
||||
* * U_i = U * (1-2^63) (for i=63)
|
||||
* where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0.
|
||||
* For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is
|
||||
* precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63).
|
||||
* * Break up the multiplicand into groups of PREC_B bits, called n_0, n_1, n_2, ..., n_(PREC_N-1).
|
||||
* * Compute sum(n_i * (PREC_G)^i * G + U_i, i=0 ... PREC_N-1), where:
|
||||
* * U_i = U * 2^i, for i=0 ... PREC_N-2
|
||||
* * U_i = U * (1-2^(PREC_N-1)), for i=PREC_N-1
|
||||
* where U is a point with no known corresponding scalar. Note that sum(U_i, i=0 ... PREC_N-1) = 0.
|
||||
* For each i, and each of the PREC_G possible values of n_i, (n_i * (PREC_G)^i * G + U_i) is
|
||||
* precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0 ... PREC_N-1).
|
||||
* None of the resulting prec group elements have a known scalar, and neither do any of
|
||||
* the intermediate sums while computing a*G.
|
||||
*/
|
||||
secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */
|
||||
secp256k1_ge_storage (*prec)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G]; /* prec[j][i] = (PREC_G)^j * i * G + U_i */
|
||||
secp256k1_scalar blind;
|
||||
secp256k1_gej initial;
|
||||
} secp256k1_ecmult_gen_context;
|
||||
|
||||
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
|
||||
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx);
|
||||
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb);
|
||||
static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
|
||||
const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb);
|
||||
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc);
|
||||
static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src);
|
||||
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx);
|
||||
static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef SECP256K1_ECMULT_GEN_IMPL_H
|
||||
#define SECP256K1_ECMULT_GEN_IMPL_H
|
||||
|
||||
#include "util.h"
|
||||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
#include "ecmult_gen.h"
|
||||
|
|
@ -14,23 +15,32 @@
|
|||
#ifdef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
#include "ecmult_static_context.h"
|
||||
#endif
|
||||
|
||||
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec));
|
||||
#else
|
||||
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0;
|
||||
#endif
|
||||
|
||||
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) {
|
||||
ctx->prec = NULL;
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) {
|
||||
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) {
|
||||
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
secp256k1_ge prec[1024];
|
||||
secp256k1_ge prec[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G];
|
||||
secp256k1_gej gj;
|
||||
secp256k1_gej nums_gej;
|
||||
int i, j;
|
||||
size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
|
||||
void* const base = *prealloc;
|
||||
#endif
|
||||
|
||||
if (ctx->prec != NULL) {
|
||||
return;
|
||||
}
|
||||
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec));
|
||||
ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
|
||||
|
||||
/* get the generator */
|
||||
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
|
||||
|
|
@ -54,39 +64,39 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
|
|||
|
||||
/* compute prec. */
|
||||
{
|
||||
secp256k1_gej precj[1024]; /* Jacobian versions of prec. */
|
||||
secp256k1_gej precj[ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G]; /* Jacobian versions of prec. */
|
||||
secp256k1_gej gbase;
|
||||
secp256k1_gej numsbase;
|
||||
gbase = gj; /* 16^j * G */
|
||||
gbase = gj; /* PREC_G^j * G */
|
||||
numsbase = nums_gej; /* 2^j * nums. */
|
||||
for (j = 0; j < 64; j++) {
|
||||
/* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */
|
||||
precj[j*16] = numsbase;
|
||||
for (i = 1; i < 16; i++) {
|
||||
secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL);
|
||||
for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
|
||||
/* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */
|
||||
precj[j*ECMULT_GEN_PREC_G] = numsbase;
|
||||
for (i = 1; i < ECMULT_GEN_PREC_G; i++) {
|
||||
secp256k1_gej_add_var(&precj[j*ECMULT_GEN_PREC_G + i], &precj[j*ECMULT_GEN_PREC_G + i - 1], &gbase, NULL);
|
||||
}
|
||||
/* Multiply gbase by 16. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* Multiply gbase by PREC_G. */
|
||||
for (i = 0; i < ECMULT_GEN_PREC_B; i++) {
|
||||
secp256k1_gej_double_var(&gbase, &gbase, NULL);
|
||||
}
|
||||
/* Multiply numbase by 2. */
|
||||
secp256k1_gej_double_var(&numsbase, &numsbase, NULL);
|
||||
if (j == 62) {
|
||||
if (j == ECMULT_GEN_PREC_N - 2) {
|
||||
/* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
|
||||
secp256k1_gej_neg(&numsbase, &numsbase);
|
||||
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
|
||||
}
|
||||
}
|
||||
secp256k1_ge_set_all_gej_var(prec, precj, 1024);
|
||||
secp256k1_ge_set_all_gej_var(prec, precj, ECMULT_GEN_PREC_N * ECMULT_GEN_PREC_G);
|
||||
}
|
||||
for (j = 0; j < 64; j++) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]);
|
||||
for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
|
||||
for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
|
||||
secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*ECMULT_GEN_PREC_G + i]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)cb;
|
||||
ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context;
|
||||
(void)prealloc;
|
||||
ctx->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])secp256k1_ecmult_static_context;
|
||||
#endif
|
||||
secp256k1_ecmult_gen_blind(ctx, NULL);
|
||||
}
|
||||
|
|
@ -95,27 +105,18 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont
|
|||
return ctx->prec != NULL;
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst,
|
||||
const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) {
|
||||
if (src->prec == NULL) {
|
||||
dst->prec = NULL;
|
||||
} else {
|
||||
static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) {
|
||||
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec));
|
||||
memcpy(dst->prec, src->prec, sizeof(*dst->prec));
|
||||
#else
|
||||
(void)cb;
|
||||
dst->prec = src->prec;
|
||||
#endif
|
||||
dst->initial = src->initial;
|
||||
dst->blind = src->blind;
|
||||
if (src->prec != NULL) {
|
||||
/* We cast to void* first to suppress a -Wcast-align warning. */
|
||||
dst->prec = (secp256k1_ge_storage (*)[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
|
||||
}
|
||||
#else
|
||||
(void)dst, (void)src;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
|
||||
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
free(ctx->prec);
|
||||
#endif
|
||||
secp256k1_scalar_clear(&ctx->blind);
|
||||
secp256k1_gej_clear(&ctx->initial);
|
||||
ctx->prec = NULL;
|
||||
|
|
@ -132,9 +133,9 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
|
|||
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
|
||||
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
|
||||
add.infinity = 0;
|
||||
for (j = 0; j < 64; j++) {
|
||||
bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4);
|
||||
for (i = 0; i < 16; i++) {
|
||||
for (j = 0; j < ECMULT_GEN_PREC_N; j++) {
|
||||
bits = secp256k1_scalar_get_bits(&gnb, j * ECMULT_GEN_PREC_B, ECMULT_GEN_PREC_B);
|
||||
for (i = 0; i < ECMULT_GEN_PREC_G; i++) {
|
||||
/** This uses a conditional move to avoid any secret data in array indexes.
|
||||
* _Any_ use of secret indexes has been demonstrated to result in timing
|
||||
* sidechannels, even when the cache-line access patterns are uniform.
|
||||
|
|
@ -162,7 +163,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
|
|||
secp256k1_fe s;
|
||||
unsigned char nonce32[32];
|
||||
secp256k1_rfc6979_hmac_sha256 rng;
|
||||
int retry;
|
||||
int overflow;
|
||||
unsigned char keydata[64] = {0};
|
||||
if (seed32 == NULL) {
|
||||
/* When seed is NULL, reset the initial point and blinding value. */
|
||||
|
|
@ -182,21 +183,18 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
|
|||
}
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32);
|
||||
memset(keydata, 0, sizeof(keydata));
|
||||
/* Retry for out of range results to achieve uniformity. */
|
||||
do {
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
retry = !secp256k1_fe_set_b32(&s, nonce32);
|
||||
retry |= secp256k1_fe_is_zero(&s);
|
||||
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
|
||||
/* Accept unobservably small non-uniformity. */
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
overflow = !secp256k1_fe_set_b32(&s, nonce32);
|
||||
overflow |= secp256k1_fe_is_zero(&s);
|
||||
secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow);
|
||||
/* Randomize the projection to defend against multiplier sidechannels. */
|
||||
secp256k1_gej_rescale(&ctx->initial, &s);
|
||||
secp256k1_fe_clear(&s);
|
||||
do {
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
secp256k1_scalar_set_b32(&b, nonce32, &retry);
|
||||
/* A blinding value of 0 works, but would undermine the projection hardening. */
|
||||
retry |= secp256k1_scalar_is_zero(&b);
|
||||
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
secp256k1_scalar_set_b32(&b, nonce32, NULL);
|
||||
/* A blinding value of 0 works, but would undermine the projection hardening. */
|
||||
secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
|
||||
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_ecmult_gen(ctx, &gb, &b);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "group.h"
|
||||
#include "scalar.h"
|
||||
#include "ecmult.h"
|
||||
|
|
@ -30,23 +31,35 @@
|
|||
# endif
|
||||
#else
|
||||
/* optimal for 128-bit and 256-bit exponents. */
|
||||
#define WINDOW_A 5
|
||||
/** larger numbers may result in slightly better performance, at the cost of
|
||||
exponentially larger precomputed tables. */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/** Two tables for window size 15: 1.375 MiB. */
|
||||
#define WINDOW_G 15
|
||||
#else
|
||||
/** One table for window size 16: 1.375 MiB. */
|
||||
#define WINDOW_G 16
|
||||
#endif
|
||||
# define WINDOW_A 5
|
||||
/** Larger values for ECMULT_WINDOW_SIZE result in possibly better
|
||||
* performance at the cost of an exponentially larger precomputed
|
||||
* table. The exact table size is
|
||||
* (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
|
||||
* where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
|
||||
* be larger due to platform-specific padding and alignment.
|
||||
* Two tables of this size are used (due to the endomorphism
|
||||
* optimization).
|
||||
*/
|
||||
# define WINDOW_G ECMULT_WINDOW_SIZE
|
||||
#endif
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
#define WNAF_BITS 128
|
||||
#else
|
||||
#define WNAF_BITS 256
|
||||
/* Noone will ever need more than a window size of 24. The code might
|
||||
* be correct for larger values of ECMULT_WINDOW_SIZE but this is not
|
||||
* not tested.
|
||||
*
|
||||
* The following limitations are known, and there are probably more:
|
||||
* If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
|
||||
* because the size of the memory object that we allocate (in bytes)
|
||||
* will not fit in a size_t.
|
||||
* If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
|
||||
* because certain expressions will overflow.
|
||||
*/
|
||||
#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
|
||||
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
|
||||
#endif
|
||||
|
||||
#define WNAF_BITS 128
|
||||
#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w))
|
||||
#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
|
||||
|
||||
|
|
@ -60,17 +73,9 @@
|
|||
#define PIPPENGER_MAX_BUCKET_WINDOW 12
|
||||
|
||||
/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
#define ECMULT_PIPPENGER_THRESHOLD 88
|
||||
#else
|
||||
#define ECMULT_PIPPENGER_THRESHOLD 160
|
||||
#endif
|
||||
#define ECMULT_PIPPENGER_THRESHOLD 88
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
#define ECMULT_MAX_POINTS_PER_BATCH 5000000
|
||||
#else
|
||||
#define ECMULT_MAX_POINTS_PER_BATCH 10000000
|
||||
#endif
|
||||
#define ECMULT_MAX_POINTS_PER_BATCH 5000000
|
||||
|
||||
/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
|
||||
* the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
|
||||
|
|
@ -121,7 +126,7 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec
|
|||
* It only operates on tables sized for WINDOW_A wnaf multiples.
|
||||
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
|
||||
* resulting point set to actually affine points, and stores those in pre.
|
||||
* It operates on tables of any size, but uses heap-allocated temporaries.
|
||||
* It operates on tables of any size.
|
||||
*
|
||||
* To compute a*P + b*G, we compute a table for P using the first function,
|
||||
* and for G using the second (which requires an inverse, but it only needs to
|
||||
|
|
@ -277,7 +282,8 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25
|
|||
if ((n) > 0) { \
|
||||
*(r) = (pre)[((n)-1)/2]; \
|
||||
} else { \
|
||||
secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \
|
||||
*(r) = (pre)[(-(n)-1)/2]; \
|
||||
secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
|
@ -289,19 +295,24 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp25
|
|||
secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
|
||||
} else { \
|
||||
secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
|
||||
secp256k1_ge_neg((r), (r)); \
|
||||
secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE =
|
||||
ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
|
||||
+ ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
|
||||
;
|
||||
|
||||
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
|
||||
ctx->pre_g = NULL;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
ctx->pre_g_128 = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) {
|
||||
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) {
|
||||
secp256k1_gej gj;
|
||||
void* const base = *prealloc;
|
||||
size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
|
||||
|
||||
if (ctx->pre_g != NULL) {
|
||||
return;
|
||||
|
|
@ -310,17 +321,24 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
|
|||
/* get the generator */
|
||||
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
|
||||
|
||||
ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
|
||||
{
|
||||
size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G));
|
||||
/* check for overflow */
|
||||
VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
|
||||
ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
|
||||
}
|
||||
|
||||
/* precompute the tables with odd multiples */
|
||||
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
{
|
||||
secp256k1_gej g_128j;
|
||||
int i;
|
||||
|
||||
ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G));
|
||||
size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G));
|
||||
/* check for overflow */
|
||||
VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
|
||||
ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
|
||||
|
||||
/* calculate 2^128*generator */
|
||||
g_128j = gj;
|
||||
|
|
@ -329,27 +347,16 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
|
|||
}
|
||||
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst,
|
||||
const secp256k1_ecmult_context *src, const secp256k1_callback *cb) {
|
||||
if (src->pre_g == NULL) {
|
||||
dst->pre_g = NULL;
|
||||
} else {
|
||||
size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
|
||||
dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
|
||||
memcpy(dst->pre_g, src->pre_g, size);
|
||||
static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) {
|
||||
if (src->pre_g != NULL) {
|
||||
/* We cast to void* first to suppress a -Wcast-align warning. */
|
||||
dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src));
|
||||
}
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (src->pre_g_128 == NULL) {
|
||||
dst->pre_g_128 = NULL;
|
||||
} else {
|
||||
size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
|
||||
dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
|
||||
memcpy(dst->pre_g_128, src->pre_g_128, size);
|
||||
if (src->pre_g_128 != NULL) {
|
||||
dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) {
|
||||
|
|
@ -357,10 +364,6 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
|
|||
}
|
||||
|
||||
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
|
||||
free(ctx->pre_g);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
free(ctx->pre_g_128);
|
||||
#endif
|
||||
secp256k1_ecmult_context_init(ctx);
|
||||
}
|
||||
|
||||
|
|
@ -372,7 +375,7 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
|
|||
* than the number of bits in the (absolute value) of the input.
|
||||
*/
|
||||
static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) {
|
||||
secp256k1_scalar s = *a;
|
||||
secp256k1_scalar s;
|
||||
int last_set_bit = -1;
|
||||
int bit = 0;
|
||||
int sign = 1;
|
||||
|
|
@ -385,6 +388,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
|
|||
|
||||
memset(wnaf, 0, len * sizeof(wnaf[0]));
|
||||
|
||||
s = *a;
|
||||
if (secp256k1_scalar_get_bits(&s, 255, 1)) {
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
sign = -1;
|
||||
|
|
@ -417,22 +421,17 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
|
|||
CHECK(carry == 0);
|
||||
while (bit < 256) {
|
||||
CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return last_set_bit + 1;
|
||||
}
|
||||
|
||||
struct secp256k1_strauss_point_state {
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_scalar na_1, na_lam;
|
||||
int wnaf_na_1[130];
|
||||
int wnaf_na_lam[130];
|
||||
int wnaf_na_1[129];
|
||||
int wnaf_na_lam[129];
|
||||
int bits_na_1;
|
||||
int bits_na_lam;
|
||||
#else
|
||||
int wnaf_na[256];
|
||||
int bits_na;
|
||||
#endif
|
||||
size_t input_pos;
|
||||
};
|
||||
|
||||
|
|
@ -440,58 +439,43 @@ struct secp256k1_strauss_state {
|
|||
secp256k1_gej* prej;
|
||||
secp256k1_fe* zr;
|
||||
secp256k1_ge* pre_a;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge* pre_a_lam;
|
||||
#endif
|
||||
struct secp256k1_strauss_point_state* ps;
|
||||
};
|
||||
|
||||
static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
|
||||
static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, size_t num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
|
||||
secp256k1_ge tmpa;
|
||||
secp256k1_fe Z;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/* Splitted G factors. */
|
||||
secp256k1_scalar ng_1, ng_128;
|
||||
int wnaf_ng_1[129];
|
||||
int bits_ng_1 = 0;
|
||||
int wnaf_ng_128[129];
|
||||
int bits_ng_128 = 0;
|
||||
#else
|
||||
int wnaf_ng[256];
|
||||
int bits_ng = 0;
|
||||
#endif
|
||||
int i;
|
||||
int bits = 0;
|
||||
int np;
|
||||
int no = 0;
|
||||
size_t np;
|
||||
size_t no = 0;
|
||||
|
||||
for (np = 0; np < num; ++np) {
|
||||
if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) {
|
||||
continue;
|
||||
}
|
||||
state->ps[no].input_pos = np;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
|
||||
secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]);
|
||||
|
||||
/* build wnaf representation for na_1 and na_lam. */
|
||||
state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A);
|
||||
state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A);
|
||||
VERIFY_CHECK(state->ps[no].bits_na_1 <= 130);
|
||||
VERIFY_CHECK(state->ps[no].bits_na_lam <= 130);
|
||||
state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &state->ps[no].na_1, WINDOW_A);
|
||||
state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &state->ps[no].na_lam, WINDOW_A);
|
||||
VERIFY_CHECK(state->ps[no].bits_na_1 <= 129);
|
||||
VERIFY_CHECK(state->ps[no].bits_na_lam <= 129);
|
||||
if (state->ps[no].bits_na_1 > bits) {
|
||||
bits = state->ps[no].bits_na_1;
|
||||
}
|
||||
if (state->ps[no].bits_na_lam > bits) {
|
||||
bits = state->ps[no].bits_na_lam;
|
||||
}
|
||||
#else
|
||||
/* build wnaf representation for na. */
|
||||
state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A);
|
||||
if (state->ps[no].bits_na > bits) {
|
||||
bits = state->ps[no].bits_na;
|
||||
}
|
||||
#endif
|
||||
++no;
|
||||
}
|
||||
|
||||
|
|
@ -523,7 +507,6 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
|
|||
secp256k1_fe_set_int(&Z, 1);
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
for (np = 0; np < no; ++np) {
|
||||
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
|
||||
secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]);
|
||||
|
|
@ -544,21 +527,12 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
|
|||
bits = bits_ng_128;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (ng) {
|
||||
bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G);
|
||||
if (bits_ng > bits) {
|
||||
bits = bits_ng;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
secp256k1_gej_set_infinity(r);
|
||||
|
||||
for (i = bits - 1; i >= 0; i--) {
|
||||
int n;
|
||||
secp256k1_gej_double_var(r, r, NULL);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
for (np = 0; np < no; ++np) {
|
||||
if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
|
||||
ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
|
||||
|
|
@ -577,18 +551,6 @@ static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, c
|
|||
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G);
|
||||
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
|
||||
}
|
||||
#else
|
||||
for (np = 0; np < no; ++np) {
|
||||
if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) {
|
||||
ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
|
||||
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
|
||||
}
|
||||
}
|
||||
if (i < bits_ng && (n = wnaf_ng[i])) {
|
||||
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
|
||||
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!r->infinity) {
|
||||
|
|
@ -601,76 +563,67 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
|
|||
secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
struct secp256k1_strauss_point_state ps[1];
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
#endif
|
||||
struct secp256k1_strauss_state state;
|
||||
|
||||
state.prej = prej;
|
||||
state.zr = zr;
|
||||
state.pre_a = pre_a;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
state.pre_a_lam = pre_a_lam;
|
||||
#endif
|
||||
state.ps = ps;
|
||||
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng);
|
||||
}
|
||||
|
||||
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
|
||||
#else
|
||||
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
|
||||
#endif
|
||||
return n_points*point_size;
|
||||
}
|
||||
|
||||
static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
|
||||
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
|
||||
secp256k1_gej* points;
|
||||
secp256k1_scalar* scalars;
|
||||
struct secp256k1_strauss_state state;
|
||||
size_t i;
|
||||
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
|
||||
|
||||
secp256k1_gej_set_infinity(r);
|
||||
if (inp_g_sc == NULL && n_points == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
|
||||
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
|
||||
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
|
||||
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
|
||||
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
|
||||
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
|
||||
state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
|
||||
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
|
||||
|
||||
if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) {
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 0;
|
||||
}
|
||||
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
|
||||
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
|
||||
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
|
||||
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
|
||||
state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
|
||||
#else
|
||||
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
|
||||
#endif
|
||||
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
|
||||
|
||||
for (i = 0; i < n_points; i++) {
|
||||
secp256k1_ge point;
|
||||
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
|
||||
secp256k1_scratch_deallocate_frame(scratch);
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&points[i], &point);
|
||||
}
|
||||
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
|
||||
secp256k1_scratch_deallocate_frame(scratch);
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wrapper for secp256k1_ecmult_multi_func interface */
|
||||
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
|
||||
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
|
||||
}
|
||||
|
||||
static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) {
|
||||
return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
|
||||
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
|
||||
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
|
||||
}
|
||||
|
||||
/** Convert a number to WNAF notation.
|
||||
|
|
@ -841,7 +794,6 @@ static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_wi
|
|||
* set of buckets) for a given number of points.
|
||||
*/
|
||||
static int secp256k1_pippenger_bucket_window(size_t n) {
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
} else if (n <= 4) {
|
||||
|
|
@ -865,33 +817,6 @@ static int secp256k1_pippenger_bucket_window(size_t n) {
|
|||
} else {
|
||||
return PIPPENGER_MAX_BUCKET_WINDOW;
|
||||
}
|
||||
#else
|
||||
if (n <= 1) {
|
||||
return 1;
|
||||
} else if (n <= 11) {
|
||||
return 2;
|
||||
} else if (n <= 45) {
|
||||
return 3;
|
||||
} else if (n <= 100) {
|
||||
return 4;
|
||||
} else if (n <= 275) {
|
||||
return 5;
|
||||
} else if (n <= 625) {
|
||||
return 6;
|
||||
} else if (n <= 1850) {
|
||||
return 7;
|
||||
} else if (n <= 3400) {
|
||||
return 8;
|
||||
} else if (n <= 9630) {
|
||||
return 9;
|
||||
} else if (n <= 17900) {
|
||||
return 10;
|
||||
} else if (n <= 32800) {
|
||||
return 11;
|
||||
} else {
|
||||
return PIPPENGER_MAX_BUCKET_WINDOW;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -899,7 +824,6 @@ static int secp256k1_pippenger_bucket_window(size_t n) {
|
|||
*/
|
||||
static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
|
||||
switch(bucket_window) {
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
case 1: return 1;
|
||||
case 2: return 4;
|
||||
case 3: return 20;
|
||||
|
|
@ -912,26 +836,11 @@ static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
|
|||
case 10: return 7880;
|
||||
case 11: return 16050;
|
||||
case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
|
||||
#else
|
||||
case 1: return 1;
|
||||
case 2: return 11;
|
||||
case 3: return 45;
|
||||
case 4: return 100;
|
||||
case 5: return 275;
|
||||
case 6: return 625;
|
||||
case 7: return 1850;
|
||||
case 8: return 3400;
|
||||
case 9: return 9630;
|
||||
case 10: return 17900;
|
||||
case 11: return 32800;
|
||||
case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
|
||||
secp256k1_scalar tmp = *s1;
|
||||
secp256k1_scalar_split_lambda(s1, s2, &tmp);
|
||||
|
|
@ -946,31 +855,23 @@ SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, s
|
|||
secp256k1_ge_neg(p2, p2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the scratch size required for a given number of points (excluding
|
||||
* base point G) without considering alignment.
|
||||
*/
|
||||
static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
size_t entries = 2*n_points + 2;
|
||||
#else
|
||||
size_t entries = n_points + 1;
|
||||
#endif
|
||||
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
|
||||
return ((1<<bucket_window) * sizeof(secp256k1_gej) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size);
|
||||
return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
|
||||
}
|
||||
|
||||
static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
|
||||
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
|
||||
static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
|
||||
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
|
||||
/* Use 2(n+1) with the endomorphism, when calculating batch
|
||||
* sizes. The reason for +1 is that we add the G scalar to the list of
|
||||
* other scalars. */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
size_t entries = 2*n_points + 2;
|
||||
#else
|
||||
size_t entries = n_points + 1;
|
||||
#endif
|
||||
secp256k1_ge *points;
|
||||
secp256k1_scalar *scalars;
|
||||
secp256k1_gej *buckets;
|
||||
|
|
@ -987,36 +888,38 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
|
|||
}
|
||||
|
||||
bucket_window = secp256k1_pippenger_bucket_window(n_points);
|
||||
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
|
||||
points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
|
||||
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
|
||||
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
|
||||
if (points == NULL || scalars == NULL || state_space == NULL) {
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
|
||||
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
|
||||
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
|
||||
if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 0;
|
||||
}
|
||||
points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
|
||||
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
|
||||
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
|
||||
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
|
||||
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
|
||||
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, (1<<bucket_window) * sizeof(*buckets));
|
||||
|
||||
if (inp_g_sc != NULL) {
|
||||
scalars[0] = *inp_g_sc;
|
||||
points[0] = secp256k1_ge_const_g;
|
||||
idx++;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
|
||||
idx++;
|
||||
#endif
|
||||
}
|
||||
|
||||
while (point_idx < n_points) {
|
||||
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
|
||||
secp256k1_scratch_deallocate_frame(scratch);
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 0;
|
||||
}
|
||||
idx++;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
|
||||
idx++;
|
||||
#endif
|
||||
point_idx++;
|
||||
}
|
||||
|
||||
|
|
@ -1033,13 +936,13 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
|
|||
for(i = 0; i < 1<<bucket_window; i++) {
|
||||
secp256k1_gej_clear(&buckets[i]);
|
||||
}
|
||||
secp256k1_scratch_deallocate_frame(scratch);
|
||||
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wrapper for secp256k1_ecmult_multi_func interface */
|
||||
static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
|
||||
static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
return secp256k1_ecmult_pippenger_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1047,8 +950,8 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_contex
|
|||
* a given scratch space. The function ensures that fewer points may also be
|
||||
* used.
|
||||
*/
|
||||
static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
|
||||
size_t max_alloc = secp256k1_scratch_max_allocation(scratch, PIPPENGER_SCRATCH_OBJECTS);
|
||||
static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
|
||||
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
|
||||
int bucket_window;
|
||||
size_t res = 0;
|
||||
|
||||
|
|
@ -1059,10 +962,8 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
|
|||
size_t space_overhead;
|
||||
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
entry_size = 2*entry_size;
|
||||
#endif
|
||||
space_overhead = ((1<<bucket_window) * sizeof(secp256k1_gej) + entry_size + sizeof(struct secp256k1_pippenger_state));
|
||||
space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
|
||||
if (space_overhead > max_alloc) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -1083,12 +984,58 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
|
|||
return res;
|
||||
}
|
||||
|
||||
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
|
||||
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
/* Computes ecmult_multi by simply multiplying and adding each point. Does not
|
||||
* require a scratch space */
|
||||
static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) {
|
||||
size_t point_idx;
|
||||
secp256k1_scalar szero;
|
||||
secp256k1_gej tmpj;
|
||||
|
||||
secp256k1_scalar_set_int(&szero, 0);
|
||||
secp256k1_gej_set_infinity(r);
|
||||
secp256k1_gej_set_infinity(&tmpj);
|
||||
/* r = inp_g_sc*G */
|
||||
secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc);
|
||||
for (point_idx = 0; point_idx < n_points; point_idx++) {
|
||||
secp256k1_ge point;
|
||||
secp256k1_gej pointj;
|
||||
secp256k1_scalar scalar;
|
||||
if (!cb(&scalar, &point, point_idx, cbdata)) {
|
||||
return 0;
|
||||
}
|
||||
/* r += scalar*point */
|
||||
secp256k1_gej_set_ge(&pointj, &point);
|
||||
secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL);
|
||||
secp256k1_gej_add_var(r, r, &tmpj, NULL);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compute the number of batches and the batch size given the maximum batch size and the
|
||||
* total number of points */
|
||||
static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) {
|
||||
if (max_n_batch_points == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) {
|
||||
max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
|
||||
}
|
||||
if (n == 0) {
|
||||
*n_batches = 0;
|
||||
*n_batch_points = 0;
|
||||
return 1;
|
||||
}
|
||||
/* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */
|
||||
*n_batches = 1 + (n - 1) / max_n_batch_points;
|
||||
*n_batch_points = 1 + (n - 1) / *n_batches;
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
|
||||
static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
|
||||
size_t i;
|
||||
|
||||
int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
|
||||
size_t max_points;
|
||||
int (*f)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
|
||||
size_t n_batches;
|
||||
size_t n_batch_points;
|
||||
|
||||
|
|
@ -1101,32 +1048,30 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
|
|||
secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
max_points = secp256k1_pippenger_max_points(scratch);
|
||||
if (max_points == 0) {
|
||||
return 0;
|
||||
} else if (max_points > ECMULT_MAX_POINTS_PER_BATCH) {
|
||||
max_points = ECMULT_MAX_POINTS_PER_BATCH;
|
||||
if (scratch == NULL) {
|
||||
return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
|
||||
}
|
||||
n_batches = (n+max_points-1)/max_points;
|
||||
n_batch_points = (n+n_batches-1)/n_batches;
|
||||
|
||||
/* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than
|
||||
* a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm.
|
||||
* As a first step check if there's enough space for Pippenger's algo (which requires less space
|
||||
* than Strauss' algo) and if not, use the simple algorithm. */
|
||||
if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) {
|
||||
return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
|
||||
}
|
||||
if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
|
||||
f = secp256k1_ecmult_pippenger_batch;
|
||||
} else {
|
||||
max_points = secp256k1_strauss_max_points(scratch);
|
||||
if (max_points == 0) {
|
||||
return 0;
|
||||
if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) {
|
||||
return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
|
||||
}
|
||||
n_batches = (n+max_points-1)/max_points;
|
||||
n_batch_points = (n+n_batches-1)/n_batches;
|
||||
f = secp256k1_ecmult_strauss_batch;
|
||||
}
|
||||
for(i = 0; i < n_batches; i++) {
|
||||
size_t nbp = n < n_batch_points ? n : n_batch_points;
|
||||
size_t offset = n_batch_points*i;
|
||||
secp256k1_gej tmp;
|
||||
if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
|
||||
if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_add_var(r, r, &tmp, NULL);
|
||||
|
|
|
|||
26
src/field.h
26
src/field.h
|
|
@ -22,20 +22,22 @@
|
|||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_FIELD_10X26)
|
||||
#include "field_10x26.h"
|
||||
#elif defined(USE_FIELD_5X52)
|
||||
#include "field_5x52.h"
|
||||
#else
|
||||
#error "Please select field implementation"
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/** Normalize a field element. */
|
||||
#if defined(SECP256K1_WIDEMUL_INT128)
|
||||
#include "field_5x52.h"
|
||||
#elif defined(SECP256K1_WIDEMUL_INT64)
|
||||
#include "field_10x26.h"
|
||||
#else
|
||||
#error "Please select wide multiplication implementation"
|
||||
#endif
|
||||
|
||||
/** Normalize a field element. This brings the field element to a canonical representation, reduces
|
||||
* its magnitude to 1, and reduces it modulo field size `p`.
|
||||
*/
|
||||
static void secp256k1_fe_normalize(secp256k1_fe *r);
|
||||
|
||||
/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */
|
||||
/** Weakly normalize a field element: reduce its magnitude to 1, but don't fully normalize. */
|
||||
static void secp256k1_fe_normalize_weak(secp256k1_fe *r);
|
||||
|
||||
/** Normalize a field element, without constant-time guarantee. */
|
||||
|
|
@ -123,10 +125,10 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe
|
|||
/** Convert a field element back from the storage type. */
|
||||
static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a);
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
|
||||
static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag);
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
|
||||
static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
|
||||
|
||||
#endif /* SECP256K1_FIELD_H */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* X = sum(i=0..9, elem[i]*2^26) mod n */
|
||||
/* X = sum(i=0..9, n[i]*2^(i*26)) mod p
|
||||
* where p = 2^256 - 0x1000003D1
|
||||
*/
|
||||
uint32_t n[10];
|
||||
#ifdef VERIFY
|
||||
int magnitude;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#define SECP256K1_FIELD_REPR_IMPL_H
|
||||
|
||||
#include "util.h"
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
|
||||
#ifdef VERIFY
|
||||
|
|
@ -321,6 +320,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
|
|||
}
|
||||
|
||||
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
|
||||
int ret;
|
||||
r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24);
|
||||
r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22);
|
||||
r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20);
|
||||
|
|
@ -332,15 +332,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
|
|||
r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24);
|
||||
r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14);
|
||||
|
||||
if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) {
|
||||
return 0;
|
||||
}
|
||||
ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
|
||||
#ifdef VERIFY
|
||||
r->magnitude = 1;
|
||||
r->normalized = 1;
|
||||
secp256k1_fe_verify(r);
|
||||
if (ret) {
|
||||
r->normalized = 1;
|
||||
secp256k1_fe_verify(r);
|
||||
} else {
|
||||
r->normalized = 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
|
||||
|
|
@ -486,7 +488,8 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t
|
|||
VERIFY_BITS(b[9], 26);
|
||||
|
||||
/** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n.
|
||||
* px is a shorthand for sum(a[i]*b[x-i], i=0..x).
|
||||
* for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
|
||||
* for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9)
|
||||
* Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0].
|
||||
*/
|
||||
|
||||
|
|
@ -1069,6 +1072,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
|
|||
secp256k1_fe_verify(a);
|
||||
secp256k1_fe_verify(b);
|
||||
VERIFY_CHECK(r != b);
|
||||
VERIFY_CHECK(a != b);
|
||||
#endif
|
||||
secp256k1_fe_mul_inner(r->n, a->n, b->n);
|
||||
#ifdef VERIFY
|
||||
|
|
@ -1093,6 +1097,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
|
||||
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
|
||||
uint32_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->n, sizeof(r->n));
|
||||
mask0 = flag + ~((uint32_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
|
||||
|
|
@ -1106,15 +1111,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
|
|||
r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1);
|
||||
r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1);
|
||||
#ifdef VERIFY
|
||||
if (a->magnitude > r->magnitude) {
|
||||
if (flag) {
|
||||
r->magnitude = a->magnitude;
|
||||
r->normalized = a->normalized;
|
||||
}
|
||||
r->normalized &= a->normalized;
|
||||
#endif
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
|
||||
uint32_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->n, sizeof(r->n));
|
||||
mask0 = flag + ~((uint32_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* X = sum(i=0..4, elem[i]*2^52) mod n */
|
||||
/* X = sum(i=0..4, n[i]*2^(i*52)) mod p
|
||||
* where p = 2^256 - 0x1000003D1
|
||||
*/
|
||||
uint64_t n[5];
|
||||
#ifdef VERIFY
|
||||
int magnitude;
|
||||
|
|
@ -44,4 +46,10 @@ typedef struct {
|
|||
(d6) | (((uint64_t)(d7)) << 32) \
|
||||
}}
|
||||
|
||||
#define SECP256K1_FE_STORAGE_CONST_GET(d) \
|
||||
(uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \
|
||||
(uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \
|
||||
(uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \
|
||||
(uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0]
|
||||
|
||||
#endif /* SECP256K1_FIELD_REPR_H */
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#endif
|
||||
|
||||
#include "util.h"
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
|
||||
#if defined(USE_ASM_X86_64)
|
||||
|
|
@ -284,6 +283,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
|
|||
}
|
||||
|
||||
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
|
||||
int ret;
|
||||
r->n[0] = (uint64_t)a[31]
|
||||
| ((uint64_t)a[30] << 8)
|
||||
| ((uint64_t)a[29] << 16)
|
||||
|
|
@ -318,15 +318,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
|
|||
| ((uint64_t)a[2] << 24)
|
||||
| ((uint64_t)a[1] << 32)
|
||||
| ((uint64_t)a[0] << 40);
|
||||
if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) {
|
||||
return 0;
|
||||
}
|
||||
ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL));
|
||||
#ifdef VERIFY
|
||||
r->magnitude = 1;
|
||||
r->normalized = 1;
|
||||
secp256k1_fe_verify(r);
|
||||
if (ret) {
|
||||
r->normalized = 1;
|
||||
secp256k1_fe_verify(r);
|
||||
} else {
|
||||
r->normalized = 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
|
||||
|
|
@ -422,6 +424,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
|
|||
secp256k1_fe_verify(a);
|
||||
secp256k1_fe_verify(b);
|
||||
VERIFY_CHECK(r != b);
|
||||
VERIFY_CHECK(a != b);
|
||||
#endif
|
||||
secp256k1_fe_mul_inner(r->n, a->n, b->n);
|
||||
#ifdef VERIFY
|
||||
|
|
@ -446,6 +449,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
|
||||
static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
|
||||
uint64_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->n, sizeof(r->n));
|
||||
mask0 = flag + ~((uint64_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
|
||||
|
|
@ -454,15 +458,16 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
|
|||
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
|
||||
r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1);
|
||||
#ifdef VERIFY
|
||||
if (a->magnitude > r->magnitude) {
|
||||
if (flag) {
|
||||
r->magnitude = a->magnitude;
|
||||
r->normalized = a->normalized;
|
||||
}
|
||||
r->normalized &= a->normalized;
|
||||
#endif
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
|
||||
uint64_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->n, sizeof(r->n));
|
||||
mask0 = flag + ~((uint64_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
|
||||
|
|
|
|||
|
|
@ -32,9 +32,11 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
|
|||
VERIFY_BITS(b[3], 56);
|
||||
VERIFY_BITS(b[4], 52);
|
||||
VERIFY_CHECK(r != b);
|
||||
VERIFY_CHECK(a != b);
|
||||
|
||||
/* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
|
||||
* px is a shorthand for sum(a[i]*b[x-i], i=0..x).
|
||||
* for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
|
||||
* for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4)
|
||||
* Note that [x 0 0 0 0 0] = [x*R].
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@
|
|||
#endif
|
||||
|
||||
#include "util.h"
|
||||
#include "num.h"
|
||||
|
||||
#if defined(USE_FIELD_10X26)
|
||||
#include "field_10x26_impl.h"
|
||||
#elif defined(USE_FIELD_5X52)
|
||||
#if defined(SECP256K1_WIDEMUL_INT128)
|
||||
#include "field_5x52_impl.h"
|
||||
#elif defined(SECP256K1_WIDEMUL_INT64)
|
||||
#include "field_10x26_impl.h"
|
||||
#else
|
||||
#error "Please select field implementation"
|
||||
#error "Please select wide multiplication implementation"
|
||||
#endif
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
|
||||
|
|
@ -48,6 +49,8 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
|
||||
int j;
|
||||
|
||||
VERIFY_CHECK(r != a);
|
||||
|
||||
/** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
|
||||
* { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
|
||||
* 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
|
||||
|
|
@ -312,4 +315,6 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
|
||||
|
||||
#endif /* SECP256K1_FIELD_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -4,10 +4,17 @@
|
|||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
// Autotools creates libsecp256k1-config.h, of which ECMULT_GEN_PREC_BITS is needed.
|
||||
// ifndef guard so downstream users can define their own if they do not use autotools.
|
||||
#if !defined(ECMULT_GEN_PREC_BITS)
|
||||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
#define USE_BASIC_CONFIG 1
|
||||
|
||||
#include "basic-config.h"
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "assumptions.h"
|
||||
#include "util.h"
|
||||
#include "field_impl.h"
|
||||
#include "scalar_impl.h"
|
||||
#include "group_impl.h"
|
||||
|
|
@ -26,6 +33,7 @@ static const secp256k1_callback default_error_callback = {
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
secp256k1_ecmult_gen_context ctx;
|
||||
void *prealloc, *base;
|
||||
int inner;
|
||||
int outer;
|
||||
FILE* fp;
|
||||
|
|
@ -38,26 +46,31 @@ int main(int argc, char **argv) {
|
|||
fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
|
||||
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
|
||||
fprintf(fp, "#include \"src/group.h\"\n");
|
||||
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
|
||||
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n");
|
||||
fprintf(fp, "#if ECMULT_GEN_PREC_N != %d || ECMULT_GEN_PREC_G != %d\n", ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G);
|
||||
fprintf(fp, " #error configuration mismatch, invalid ECMULT_GEN_PREC_N, ECMULT_GEN_PREC_G. Try deleting ecmult_static_context.h before the build.\n");
|
||||
fprintf(fp, "#endif\n");
|
||||
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[ECMULT_GEN_PREC_N][ECMULT_GEN_PREC_G] = {\n");
|
||||
|
||||
base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE);
|
||||
prealloc = base;
|
||||
secp256k1_ecmult_gen_context_init(&ctx);
|
||||
secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback);
|
||||
for(outer = 0; outer != 64; outer++) {
|
||||
secp256k1_ecmult_gen_context_build(&ctx, &prealloc);
|
||||
for(outer = 0; outer != ECMULT_GEN_PREC_N; outer++) {
|
||||
fprintf(fp,"{\n");
|
||||
for(inner = 0; inner != 16; inner++) {
|
||||
for(inner = 0; inner != ECMULT_GEN_PREC_G; inner++) {
|
||||
fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner]));
|
||||
if (inner != 15) {
|
||||
if (inner != ECMULT_GEN_PREC_G - 1) {
|
||||
fprintf(fp,",\n");
|
||||
} else {
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
}
|
||||
if (outer != 63) {
|
||||
if (outer != ECMULT_GEN_PREC_N - 1) {
|
||||
fprintf(fp,"},\n");
|
||||
} else {
|
||||
fprintf(fp,"}\n");
|
||||
|
|
@ -65,10 +78,11 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
fprintf(fp,"};\n");
|
||||
secp256k1_ecmult_gen_context_clear(&ctx);
|
||||
|
||||
free(base);
|
||||
|
||||
fprintf(fp, "#undef SC\n");
|
||||
fprintf(fp, "#endif\n");
|
||||
fclose(fp);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
27
src/group.h
27
src/group.h
|
|
@ -59,6 +59,7 @@ static int secp256k1_ge_is_infinity(const secp256k1_ge *a);
|
|||
/** Check whether a group element is valid (i.e., on the curve). */
|
||||
static int secp256k1_ge_is_valid_var(const secp256k1_ge *a);
|
||||
|
||||
/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */
|
||||
static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
|
||||
|
||||
/** Set a group element equal to another which is given in jacobian coordinates */
|
||||
|
|
@ -95,14 +96,13 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
|
|||
/** Check whether a group element's y coordinate is a quadratic residue. */
|
||||
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
|
||||
|
||||
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0).
|
||||
* a may not be zero. Constant time. */
|
||||
static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
|
||||
/** Set r equal to the double of a. Constant time. */
|
||||
static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
|
||||
|
||||
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */
|
||||
/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
|
||||
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
|
||||
|
||||
/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
|
||||
/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
|
||||
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr);
|
||||
|
||||
/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */
|
||||
|
|
@ -110,16 +110,14 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
|
|||
|
||||
/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient
|
||||
than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time
|
||||
guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */
|
||||
guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */
|
||||
static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr);
|
||||
|
||||
/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */
|
||||
static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */
|
||||
static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a);
|
||||
#endif
|
||||
|
||||
/** Clear a secp256k1_gej to prevent leaking sensitive information. */
|
||||
static void secp256k1_gej_clear(secp256k1_gej *r);
|
||||
|
|
@ -133,10 +131,21 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge
|
|||
/** Convert a group element back from the storage type. */
|
||||
static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a);
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
|
||||
static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag);
|
||||
|
||||
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
|
||||
static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b);
|
||||
|
||||
/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve.
|
||||
*
|
||||
* In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the
|
||||
* group, and this function returns always true.
|
||||
*
|
||||
* When compiling in exhaustive test mode, a slightly different curve equation is used, leading to a group with a
|
||||
* (very) small subgroup, and that subgroup is what is used for all cryptographic operations. In that mode, this
|
||||
* function checks whether a point that is on the curve is in fact also in that subgroup.
|
||||
*/
|
||||
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge);
|
||||
|
||||
#endif /* SECP256K1_GROUP_H */
|
||||
|
|
|
|||
184
src/group_impl.h
184
src/group_impl.h
|
|
@ -11,49 +11,38 @@
|
|||
#include "field.h"
|
||||
#include "group.h"
|
||||
|
||||
/* These points can be generated in sage as follows:
|
||||
/* These exhaustive group test orders and generators are chosen such that:
|
||||
* - The field size is equal to that of secp256k1, so field code is the same.
|
||||
* - The curve equation is of the form y^2=x^3+B for some constant B.
|
||||
* - The subgroup has a generator 2*P, where P.x=1.
|
||||
* - The subgroup has size less than 1000 to permit exhaustive testing.
|
||||
* - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).
|
||||
*
|
||||
* 0. Setup a worksheet with the following parameters.
|
||||
* b = 4 # whatever CURVE_B will be set to
|
||||
* F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
|
||||
* C = EllipticCurve ([F (0), F (b)])
|
||||
*
|
||||
* 1. Determine all the small orders available to you. (If there are
|
||||
* no satisfactory ones, go back and change b.)
|
||||
* print C.order().factor(limit=1000)
|
||||
*
|
||||
* 2. Choose an order as one of the prime factors listed in the above step.
|
||||
* (You can also multiply some to get a composite order, though the
|
||||
* tests will crash trying to invert scalars during signing.) We take a
|
||||
* random point and scale it to drop its order to the desired value.
|
||||
* There is some probability this won't work; just try again.
|
||||
* order = 199
|
||||
* P = C.random_point()
|
||||
* P = (int(P.order()) / int(order)) * P
|
||||
* assert(P.order() == order)
|
||||
*
|
||||
* 3. Print the values. You'll need to use a vim macro or something to
|
||||
* split the hex output into 4-byte chunks.
|
||||
* print "%x %x" % P.xy()
|
||||
* These parameters are generated using sage/gen_exhaustive_groups.sage.
|
||||
*/
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
# if EXHAUSTIVE_TEST_ORDER == 199
|
||||
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
|
||||
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
|
||||
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
|
||||
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
|
||||
# if EXHAUSTIVE_TEST_ORDER == 13
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,
|
||||
0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,
|
||||
0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,
|
||||
0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24
|
||||
);
|
||||
|
||||
const int CURVE_B = 4;
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 13
|
||||
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
|
||||
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
|
||||
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
|
||||
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
|
||||
0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
|
||||
0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
|
||||
);
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 199
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,
|
||||
0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,
|
||||
0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,
|
||||
0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae
|
||||
);
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
|
||||
0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1,
|
||||
0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb
|
||||
);
|
||||
const int CURVE_B = 2;
|
||||
# else
|
||||
# error No known generator for the specified exhaustive test group order.
|
||||
# endif
|
||||
|
|
@ -68,7 +57,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
|||
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
|
||||
);
|
||||
|
||||
const int CURVE_B = 7;
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7);
|
||||
#endif
|
||||
|
||||
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
|
||||
|
|
@ -175,6 +164,8 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
|
|||
/* The z of the final point gives us the "global Z" for the table. */
|
||||
r[i].x = a[i].x;
|
||||
r[i].y = a[i].y;
|
||||
/* Ensure all y values are in weak normal form for fast negation of points */
|
||||
secp256k1_fe_normalize_weak(&r[i].y);
|
||||
*globalz = a[i].z;
|
||||
r[i].infinity = 0;
|
||||
zs = zr[i];
|
||||
|
|
@ -217,14 +208,13 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
|
|||
}
|
||||
|
||||
static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) {
|
||||
secp256k1_fe x2, x3, c;
|
||||
secp256k1_fe x2, x3;
|
||||
r->x = *x;
|
||||
secp256k1_fe_sqr(&x2, x);
|
||||
secp256k1_fe_mul(&x3, x, &x2);
|
||||
r->infinity = 0;
|
||||
secp256k1_fe_set_int(&c, CURVE_B);
|
||||
secp256k1_fe_add(&c, &x3);
|
||||
return secp256k1_fe_sqrt(&r->y, &c);
|
||||
secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
|
||||
return secp256k1_fe_sqrt(&r->y, &x3);
|
||||
}
|
||||
|
||||
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
|
||||
|
|
@ -267,41 +257,20 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a) {
|
|||
return a->infinity;
|
||||
}
|
||||
|
||||
static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
|
||||
secp256k1_fe y2, x3, z2, z6;
|
||||
if (a->infinity) {
|
||||
return 0;
|
||||
}
|
||||
/** y^2 = x^3 + 7
|
||||
* (Y/Z^3)^2 = (X/Z^2)^3 + 7
|
||||
* Y^2 / Z^6 = X^3 / Z^6 + 7
|
||||
* Y^2 = X^3 + 7*Z^6
|
||||
*/
|
||||
secp256k1_fe_sqr(&y2, &a->y);
|
||||
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
|
||||
secp256k1_fe_sqr(&z2, &a->z);
|
||||
secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
|
||||
secp256k1_fe_mul_int(&z6, CURVE_B);
|
||||
secp256k1_fe_add(&x3, &z6);
|
||||
secp256k1_fe_normalize_weak(&x3);
|
||||
return secp256k1_fe_equal_var(&y2, &x3);
|
||||
}
|
||||
|
||||
static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
|
||||
secp256k1_fe y2, x3, c;
|
||||
secp256k1_fe y2, x3;
|
||||
if (a->infinity) {
|
||||
return 0;
|
||||
}
|
||||
/* y^2 = x^3 + 7 */
|
||||
secp256k1_fe_sqr(&y2, &a->y);
|
||||
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
|
||||
secp256k1_fe_set_int(&c, CURVE_B);
|
||||
secp256k1_fe_add(&x3, &c);
|
||||
secp256k1_fe_add(&x3, &secp256k1_fe_const_b);
|
||||
secp256k1_fe_normalize_weak(&x3);
|
||||
return secp256k1_fe_equal_var(&y2, &x3);
|
||||
}
|
||||
|
||||
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
|
||||
static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) {
|
||||
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
|
||||
*
|
||||
* Note that there is an implementation described at
|
||||
|
|
@ -310,29 +279,8 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
|
|||
* mainly because it requires more normalizations.
|
||||
*/
|
||||
secp256k1_fe t1,t2,t3,t4;
|
||||
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
|
||||
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
|
||||
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
|
||||
*
|
||||
* Having said this, if this function receives a point on a sextic twist, e.g. by
|
||||
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
|
||||
* since -6 does have a cube root mod p. For this point, this function will not set
|
||||
* the infinity flag even though the point doubles to infinity, and the result
|
||||
* point will be gibberish (z = 0 but infinity = 0).
|
||||
*/
|
||||
r->infinity = a->infinity;
|
||||
if (r->infinity) {
|
||||
if (rzr != NULL) {
|
||||
secp256k1_fe_set_int(rzr, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rzr != NULL) {
|
||||
*rzr = a->y;
|
||||
secp256k1_fe_normalize_weak(rzr);
|
||||
secp256k1_fe_mul_int(rzr, 2);
|
||||
}
|
||||
r->infinity = a->infinity;
|
||||
|
||||
secp256k1_fe_mul(&r->z, &a->z, &a->y);
|
||||
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
|
||||
|
|
@ -356,9 +304,32 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
|
|||
secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
|
||||
VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
|
||||
secp256k1_gej_double_var(r, a, rzr);
|
||||
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
|
||||
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
|
||||
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
|
||||
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
|
||||
*
|
||||
* Having said this, if this function receives a point on a sextic twist, e.g. by
|
||||
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
|
||||
* since -6 does have a cube root mod p. For this point, this function will not set
|
||||
* the infinity flag even though the point doubles to infinity, and the result
|
||||
* point will be gibberish (z = 0 but infinity = 0).
|
||||
*/
|
||||
if (a->infinity) {
|
||||
r->infinity = 1;
|
||||
if (rzr != NULL) {
|
||||
secp256k1_fe_set_int(rzr, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rzr != NULL) {
|
||||
*rzr = a->y;
|
||||
secp256k1_fe_normalize_weak(rzr);
|
||||
secp256k1_fe_mul_int(rzr, 2);
|
||||
}
|
||||
|
||||
secp256k1_gej_double(r, a);
|
||||
}
|
||||
|
||||
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {
|
||||
|
|
@ -395,7 +366,7 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons
|
|||
if (rzr != NULL) {
|
||||
secp256k1_fe_set_int(rzr, 0);
|
||||
}
|
||||
r->infinity = 1;
|
||||
secp256k1_gej_set_infinity(r);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -445,7 +416,7 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c
|
|||
if (rzr != NULL) {
|
||||
secp256k1_fe_set_int(rzr, 0);
|
||||
}
|
||||
r->infinity = 1;
|
||||
secp256k1_gej_set_infinity(r);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -504,7 +475,7 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a,
|
|||
if (secp256k1_fe_normalizes_to_zero_var(&i)) {
|
||||
secp256k1_gej_double_var(r, a, NULL);
|
||||
} else {
|
||||
r->infinity = 1;
|
||||
secp256k1_gej_set_infinity(r);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -675,7 +646,6 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r,
|
|||
secp256k1_fe_storage_cmov(&r->y, &a->y, flag);
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
|
||||
static const secp256k1_fe beta = SECP256K1_FE_CONST(
|
||||
0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul,
|
||||
|
|
@ -684,7 +654,6 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
|
|||
*r = *a;
|
||||
secp256k1_fe_mul(&r->x, &r->x, &beta);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
|
||||
secp256k1_fe yz;
|
||||
|
|
@ -700,4 +669,25 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
|
|||
return secp256k1_fe_is_quad_var(&yz);
|
||||
}
|
||||
|
||||
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
|
||||
#ifdef EXHAUSTIVE_TEST_ORDER
|
||||
secp256k1_gej out;
|
||||
int i;
|
||||
|
||||
/* A very simple EC multiplication ladder that avoids a dependecy on ecmult. */
|
||||
secp256k1_gej_set_infinity(&out);
|
||||
for (i = 0; i < 32; ++i) {
|
||||
secp256k1_gej_double_var(&out, &out, NULL);
|
||||
if ((((uint32_t)EXHAUSTIVE_TEST_ORDER) >> (31 - i)) & 1) {
|
||||
secp256k1_gej_add_ge_var(&out, &out, ge, NULL);
|
||||
}
|
||||
}
|
||||
return secp256k1_gej_is_infinity(&out);
|
||||
#else
|
||||
(void)ge;
|
||||
/* The real secp256k1 group has cofactor 1, so the subgroup is the entire curve. */
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_GROUP_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define SECP256K1_HASH_IMPL_H
|
||||
|
||||
#include "hash.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -27,9 +28,9 @@
|
|||
(h) = t1 + t2; \
|
||||
} while(0)
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#if defined(SECP256K1_BIG_ENDIAN)
|
||||
#define BE32(x) (x)
|
||||
#else
|
||||
#elif defined(SECP256K1_LITTLE_ENDIAN)
|
||||
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
|
||||
#endif
|
||||
|
||||
|
|
@ -131,7 +132,8 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) {
|
|||
static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
|
||||
size_t bufsize = hash->bytes & 0x3F;
|
||||
hash->bytes += len;
|
||||
while (bufsize + len >= 64) {
|
||||
VERIFY_CHECK(hash->bytes >= len);
|
||||
while (len >= 64 - bufsize) {
|
||||
/* Fill the buffer, and process it. */
|
||||
size_t chunk_len = 64 - bufsize;
|
||||
memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len);
|
||||
|
|
@ -162,6 +164,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out
|
|||
memcpy(out32, (const unsigned char*)out, 32);
|
||||
}
|
||||
|
||||
/* Initializes a sha256 struct and writes the 64 byte string
|
||||
* SHA256(tag)||SHA256(tag) into it. */
|
||||
static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
|
||||
unsigned char buf[32];
|
||||
secp256k1_sha256_initialize(hash);
|
||||
secp256k1_sha256_write(hash, tag, taglen);
|
||||
secp256k1_sha256_finalize(hash, buf);
|
||||
|
||||
secp256k1_sha256_initialize(hash);
|
||||
secp256k1_sha256_write(hash, buf, 32);
|
||||
secp256k1_sha256_write(hash, buf, 32);
|
||||
}
|
||||
|
||||
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
|
||||
size_t n;
|
||||
unsigned char rkey[64];
|
||||
|
|
|
|||
|
|
@ -1,446 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import static org.bitcoin.NativeSecp256k1Util.*;
|
||||
|
||||
/**
|
||||
* <p>This class holds native methods to handle ECDSA verification.</p>
|
||||
*
|
||||
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
|
||||
*
|
||||
* <p>To build secp256k1 for use with bitcoinj, run
|
||||
* `./configure --enable-jni --enable-experimental --enable-module-ecdh`
|
||||
* and `make` then copy `.libs/libsecp256k1.so` to your system library path
|
||||
* or point the JVM to the folder containing it with -Djava.library.path
|
||||
* </p>
|
||||
*/
|
||||
public class NativeSecp256k1 {
|
||||
|
||||
private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
private static final Lock r = rwl.readLock();
|
||||
private static final Lock w = rwl.writeLock();
|
||||
private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
|
||||
/**
|
||||
* Verifies the given secp256k1 signature in native code.
|
||||
* Calling when enabled == false is undefined (probably library not loaded)
|
||||
*
|
||||
* @param data The data which was signed, must be exactly 32 bytes
|
||||
* @param signature The signature
|
||||
* @param pub The public key which did the signing
|
||||
*/
|
||||
public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{
|
||||
Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 520) {
|
||||
byteBuff = ByteBuffer.allocateDirect(520);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(signature);
|
||||
byteBuff.put(pub);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Create an ECDSA signature.
|
||||
*
|
||||
* @param data Message hash, 32 bytes
|
||||
* @param key Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param sig byte array of signature
|
||||
*/
|
||||
public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
|
||||
Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + 32);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(sec);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] sigArr = retByteArray[0];
|
||||
int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(sigArr.length, sigLen, "Got bad signature length.");
|
||||
|
||||
return retVal == 0 ? new byte[0] : sigArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*/
|
||||
public static boolean secKeyVerify(byte[] seckey) {
|
||||
Preconditions.checkArgument(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* libsecp256k1 Compute Pubkey - computes public key from secret key
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param pubkey ECDSA Public key, 33 or 65 bytes
|
||||
*/
|
||||
//TODO add a 'compressed' arg
|
||||
public static byte[] computePubkey(byte[] seckey) throws AssertFailException{
|
||||
Preconditions.checkArgument(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
return retVal == 0 ? new byte[0]: pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Cleanup - This destroys the secp256k1 context object
|
||||
* This should be called at the end of the program for proper cleanup of the context.
|
||||
*/
|
||||
public static synchronized void cleanup() {
|
||||
w.lock();
|
||||
try {
|
||||
secp256k1_destroy_context(Secp256k1Context.getContext());
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static long cloneContext() {
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ctx_clone(Secp256k1Context.getContext());
|
||||
} finally { r.unlock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 create ECDH secret - constant time ECDH calculation
|
||||
*
|
||||
* @param seckey byte array of secret key used in exponentiaion
|
||||
* @param pubkey byte array of public key used in exponentiaion
|
||||
*/
|
||||
public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
|
||||
Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
byteBuff.put(pubkey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] resArr = retByteArray[0];
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
|
||||
assertEquals(resArr.length, 32, "Got bad result length.");
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return resArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 randomize - updates the context randomization
|
||||
*
|
||||
* @param seed 32-byte random seed
|
||||
*/
|
||||
public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
|
||||
Preconditions.checkArgument(seed.length == 32 || seed == null);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seed.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seed.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seed);
|
||||
|
||||
w.lock();
|
||||
try {
|
||||
return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long secp256k1_ctx_clone(long context);
|
||||
|
||||
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen);
|
||||
|
||||
private static native void secp256k1_destroy_context(long context);
|
||||
|
||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
}
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
package org.bitcoin;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import java.util.Arrays;
|
||||
import java.math.BigInteger;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import static org.bitcoin.NativeSecp256k1Util.*;
|
||||
|
||||
/**
|
||||
* This class holds test cases defined for testing this library.
|
||||
*/
|
||||
public class NativeSecp256k1Test {
|
||||
|
||||
//TODO improve comments/add more tests
|
||||
/**
|
||||
* This tests verify() for a valid signature
|
||||
*/
|
||||
public static void testVerifyPos() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
assertEquals( result, true , "testVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests verify() for a non-valid signature
|
||||
*/
|
||||
public static void testVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for a valid secretkey
|
||||
*/
|
||||
public static void testSecKeyVerifyPos() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, true , "testSecKeyVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for an invalid secretkey
|
||||
*/
|
||||
public static void testSecKeyVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testSecKeyVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a valid secretkey
|
||||
*/
|
||||
public static void testPubKeyCreatePos() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a invalid secretkey
|
||||
*/
|
||||
public static void testPubKeyCreateNeg() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a valid secretkey
|
||||
*/
|
||||
public static void testSignPos() throws AssertFailException{
|
||||
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a invalid secretkey
|
||||
*/
|
||||
public static void testSignNeg() throws AssertFailException{
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "" , "testSignNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add
|
||||
*/
|
||||
public static void testPrivKeyTweakAdd_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul
|
||||
*/
|
||||
public static void testPrivKeyTweakMul_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add uncompressed
|
||||
*/
|
||||
public static void testPrivKeyTweakAdd_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul uncompressed
|
||||
*/
|
||||
public static void testPrivKeyTweakMul_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests seed randomization
|
||||
*/
|
||||
public static void testRandomize() throws AssertFailException {
|
||||
byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
|
||||
boolean result = NativeSecp256k1.randomize(seed);
|
||||
assertEquals( result, true, "testRandomize");
|
||||
}
|
||||
|
||||
public static void testCreateECDHSecret() throws AssertFailException{
|
||||
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
|
||||
String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws AssertFailException{
|
||||
|
||||
|
||||
System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");
|
||||
|
||||
assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );
|
||||
|
||||
//Test verify() success/fail
|
||||
testVerifyPos();
|
||||
testVerifyNeg();
|
||||
|
||||
//Test secKeyVerify() success/fail
|
||||
testSecKeyVerifyPos();
|
||||
testSecKeyVerifyNeg();
|
||||
|
||||
//Test computePubkey() success/fail
|
||||
testPubKeyCreatePos();
|
||||
testPubKeyCreateNeg();
|
||||
|
||||
//Test sign() success/fail
|
||||
testSignPos();
|
||||
testSignNeg();
|
||||
|
||||
//Test privKeyTweakAdd() 1
|
||||
testPrivKeyTweakAdd_1();
|
||||
|
||||
//Test privKeyTweakMul() 2
|
||||
testPrivKeyTweakMul_1();
|
||||
|
||||
//Test privKeyTweakAdd() 3
|
||||
testPrivKeyTweakAdd_2();
|
||||
|
||||
//Test privKeyTweakMul() 4
|
||||
testPrivKeyTweakMul_2();
|
||||
|
||||
//Test randomize()
|
||||
testRandomize();
|
||||
|
||||
//Test ECDH
|
||||
testCreateECDHSecret();
|
||||
|
||||
NativeSecp256k1.cleanup();
|
||||
|
||||
System.out.println(" All tests passed." );
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
public class NativeSecp256k1Util{
|
||||
|
||||
public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
|
||||
if( !val.equals(val2) )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static class AssertFailException extends Exception {
|
||||
public AssertFailException(String message) {
|
||||
super( message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
/**
|
||||
* This class holds the context reference used in native methods
|
||||
* to handle ECDSA operations.
|
||||
*/
|
||||
public class Secp256k1Context {
|
||||
private static final boolean enabled; //true if the library is loaded
|
||||
private static final long context; //ref to pointer to context obj
|
||||
|
||||
static { //static initializer
|
||||
boolean isEnabled = true;
|
||||
long contextRef = -1;
|
||||
try {
|
||||
System.loadLibrary("secp256k1");
|
||||
contextRef = secp256k1_init_context();
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.out.println("UnsatisfiedLinkError: " + e.toString());
|
||||
isEnabled = false;
|
||||
}
|
||||
enabled = isEnabled;
|
||||
context = contextRef;
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public static long getContext() {
|
||||
if(!enabled) return -1; //sanity check
|
||||
return context;
|
||||
}
|
||||
|
||||
private static native long secp256k1_init_context();
|
||||
}
|
||||
|
|
@ -1,379 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "org_bitcoin_NativeSecp256k1.h"
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_ecdh.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
||||
{
|
||||
const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
|
||||
return ctx_clone_l;
|
||||
|
||||
}
|
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return secp256k1_context_randomize(ctx, seed);
|
||||
|
||||
}
|
||||
|
||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
}
|
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* sigdata = { (unsigned char*) (data + 32) };
|
||||
const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
|
||||
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
unsigned char* secKey = (unsigned char*) (data + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray sigArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
secp256k1_ecdsa_signature sig[72];
|
||||
|
||||
int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL);
|
||||
|
||||
unsigned char outputSer[72];
|
||||
size_t outputLen = 72;
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
sigArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return secp256k1_ec_seckey_verify(ctx, secKey);
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubkeyArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
|
||||
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubkeyArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray privArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int privkeylen = 32;
|
||||
|
||||
int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
|
||||
|
||||
intsarray[0] = privkeylen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray privArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int privkeylen = 32;
|
||||
|
||||
int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
|
||||
|
||||
intsarray[0] = privkeylen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
|
||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
|
||||
}
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
||||
|
||||
if ( ret ) {
|
||||
ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
|
||||
}
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
|
||||
(JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
|
||||
{
|
||||
(void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray outArray, intsByteArray;
|
||||
unsigned char intsarray[1];
|
||||
secp256k1_pubkey pubkey;
|
||||
unsigned char nonce_res[32];
|
||||
size_t outputLen = 32;
|
||||
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
||||
|
||||
if (ret) {
|
||||
ret = secp256k1_ecdh(
|
||||
ctx,
|
||||
nonce_res,
|
||||
&pubkey,
|
||||
secdata,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
intsarray[0] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
outArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, outArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 1);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
#include "include/secp256k1.h"
|
||||
/* Header for class org_bitcoin_NativeSecp256k1 */
|
||||
|
||||
#ifndef _Included_org_bitcoin_NativeSecp256k1
|
||||
#define _Included_org_bitcoin_NativeSecp256k1
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ctx_clone
|
||||
* Signature: (J)J
|
||||
*/
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_context_randomize
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_privkey_tweak_add
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_privkey_tweak_mul
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_pubkey_tweak_add
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_pubkey_tweak_mul
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_destroy_context
|
||||
* Signature: (J)V
|
||||
*/
|
||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdsa_verify
|
||||
* Signature: (Ljava/nio/ByteBuffer;JII)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv *, jclass, jobject, jlong, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdsa_sign
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_seckey_verify
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_pubkey_create
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_pubkey_parse
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdh
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "org_bitcoin_Secp256k1Context.h"
|
||||
#include "include/secp256k1.h"
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
||||
(JNIEnv* env, jclass classObject)
|
||||
{
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
|
||||
return (uintptr_t)ctx;
|
||||
}
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
#include "include/secp256k1.h"
|
||||
/* Header for class org_bitcoin_Secp256k1Context */
|
||||
|
||||
#ifndef _Included_org_bitcoin_Secp256k1Context
|
||||
#define _Included_org_bitcoin_Secp256k1Context
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: org_bitcoin_Secp256k1Context
|
||||
* Method: secp256k1_init_context
|
||||
* Signature: ()J
|
||||
*/
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -10,14 +10,14 @@
|
|||
#include "include/secp256k1_ecdh.h"
|
||||
#include "ecmult_const_impl.h"
|
||||
|
||||
static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) {
|
||||
unsigned char version = (y[31] & 0x01) | 0x02;
|
||||
static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
|
||||
unsigned char version = (y32[31] & 0x01) | 0x02;
|
||||
secp256k1_sha256 sha;
|
||||
(void)data;
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, &version, 1);
|
||||
secp256k1_sha256_write(&sha, x, 32);
|
||||
secp256k1_sha256_write(&sha, x32, 32);
|
||||
secp256k1_sha256_finalize(&sha, output);
|
||||
|
||||
return 1;
|
||||
|
|
@ -32,36 +32,40 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se
|
|||
secp256k1_gej res;
|
||||
secp256k1_ge pt;
|
||||
secp256k1_scalar s;
|
||||
unsigned char x[32];
|
||||
unsigned char y[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output != NULL);
|
||||
ARG_CHECK(point != NULL);
|
||||
ARG_CHECK(scalar != NULL);
|
||||
|
||||
if (hashfp == NULL) {
|
||||
hashfp = secp256k1_ecdh_hash_function_default;
|
||||
}
|
||||
|
||||
secp256k1_pubkey_load(ctx, &pt, point);
|
||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
unsigned char x[32];
|
||||
unsigned char y[32];
|
||||
|
||||
secp256k1_ecmult_const(&res, &pt, &s, 256);
|
||||
secp256k1_ge_set_gej(&pt, &res);
|
||||
overflow |= secp256k1_scalar_is_zero(&s);
|
||||
secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow);
|
||||
|
||||
/* Compute a hash of the point */
|
||||
secp256k1_fe_normalize(&pt.x);
|
||||
secp256k1_fe_normalize(&pt.y);
|
||||
secp256k1_fe_get_b32(x, &pt.x);
|
||||
secp256k1_fe_get_b32(y, &pt.y);
|
||||
secp256k1_ecmult_const(&res, &pt, &s, 256);
|
||||
secp256k1_ge_set_gej(&pt, &res);
|
||||
|
||||
ret = hashfp(output, x, y, data);
|
||||
}
|
||||
/* Compute a hash of the point */
|
||||
secp256k1_fe_normalize(&pt.x);
|
||||
secp256k1_fe_normalize(&pt.y);
|
||||
secp256k1_fe_get_b32(x, &pt.x);
|
||||
secp256k1_fe_get_b32(y, &pt.y);
|
||||
|
||||
ret = hashfp(output, x, y, data);
|
||||
|
||||
memset(x, 0, 32);
|
||||
memset(y, 0, 32);
|
||||
secp256k1_scalar_clear(&s);
|
||||
return ret;
|
||||
|
||||
return !!ret & !overflow;
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_MODULE_ECDH_MAIN_H */
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ void test_ecdh_generator_basepoint(void) {
|
|||
/* compute "explicitly" */
|
||||
CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1);
|
||||
/* compare */
|
||||
CHECK(memcmp(output_ecdh, point_ser, 65) == 0);
|
||||
CHECK(secp256k1_memcmp_var(output_ecdh, point_ser, 65) == 0);
|
||||
|
||||
/* compute using ECDH function with default hash function */
|
||||
CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL, NULL) == 1);
|
||||
|
|
@ -90,7 +90,7 @@ void test_ecdh_generator_basepoint(void) {
|
|||
secp256k1_sha256_write(&sha, point_ser, point_ser_len);
|
||||
secp256k1_sha256_finalize(&sha, output_ser);
|
||||
/* compare */
|
||||
CHECK(memcmp(output_ecdh, output_ser, 32) == 0);
|
||||
CHECK(secp256k1_memcmp_var(output_ecdh, output_ser, 32) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
4
src/modules/extrakeys/Makefile.am.include
Normal file
4
src/modules/extrakeys/Makefile.am.include
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
include_HEADERS += include/secp256k1_extrakeys.h
|
||||
noinst_HEADERS += src/modules/extrakeys/tests_impl.h
|
||||
noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
|
||||
noinst_HEADERS += src/modules/extrakeys/main_impl.h
|
||||
251
src/modules/extrakeys/main_impl.h
Normal file
251
src/modules/extrakeys/main_impl.h
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2020 Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_
|
||||
#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_extrakeys.h"
|
||||
|
||||
static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
|
||||
return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey);
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) {
|
||||
secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge);
|
||||
}
|
||||
|
||||
int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) {
|
||||
secp256k1_ge pk;
|
||||
secp256k1_fe x;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ARG_CHECK(input32 != NULL);
|
||||
|
||||
if (!secp256k1_fe_set_b32(&x, input32)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ge_is_in_correct_subgroup(&pk)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_xonly_pubkey_save(pubkey, &pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) {
|
||||
secp256k1_ge pk;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output32 != NULL);
|
||||
memset(output32, 0, 32);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_get_b32(output32, &pk.x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Keeps a group element as is if it has an even Y and otherwise negates it.
|
||||
* y_parity is set to 0 in the former case and to 1 in the latter case.
|
||||
* Requires that the coordinates of r are normalized. */
|
||||
static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) {
|
||||
int y_parity = 0;
|
||||
VERIFY_CHECK(!secp256k1_ge_is_infinity(r));
|
||||
|
||||
if (secp256k1_fe_is_odd(&r->y)) {
|
||||
secp256k1_fe_negate(&r->y, &r->y, 1);
|
||||
y_parity = 1;
|
||||
}
|
||||
return y_parity;
|
||||
}
|
||||
|
||||
int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) {
|
||||
secp256k1_ge pk;
|
||||
int tmp;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(xonly_pubkey != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
tmp = secp256k1_extrakeys_ge_even_y(&pk);
|
||||
if (pk_parity != NULL) {
|
||||
*pk_parity = tmp;
|
||||
}
|
||||
secp256k1_xonly_pubkey_save(xonly_pubkey, &pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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_ge pk;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output_pubkey != NULL);
|
||||
memset(output_pubkey, 0, sizeof(*output_pubkey));
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(internal_pubkey != NULL);
|
||||
ARG_CHECK(tweak32 != NULL);
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
|
||||
|| !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_pubkey_save(output_pubkey, &pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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_ge pk;
|
||||
unsigned char pk_expected32[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(internal_pubkey != NULL);
|
||||
ARG_CHECK(tweaked_pubkey32 != NULL);
|
||||
ARG_CHECK(tweak32 != NULL);
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
|
||||
|| !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_normalize_var(&pk.x);
|
||||
secp256k1_fe_normalize_var(&pk.y);
|
||||
secp256k1_fe_get_b32(pk_expected32, &pk.x);
|
||||
|
||||
return secp256k1_memcmp_var(&pk_expected32, tweaked_pubkey32, 32) == 0
|
||||
&& secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
|
||||
}
|
||||
|
||||
static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
|
||||
secp256k1_scalar_get_b32(&keypair->data[0], sk);
|
||||
secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
|
||||
}
|
||||
|
||||
|
||||
static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
|
||||
int ret;
|
||||
|
||||
ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
|
||||
/* We can declassify ret here because sk is only zero if a keypair function
|
||||
* failed (which zeroes the keypair) and its return value is ignored. */
|
||||
secp256k1_declassify(ctx, &ret, sizeof(ret));
|
||||
ARG_CHECK(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
|
||||
* and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
|
||||
* pk with dummy values. */
|
||||
static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
|
||||
int ret;
|
||||
const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];
|
||||
|
||||
/* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
|
||||
* invalid. */
|
||||
secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
|
||||
ret = secp256k1_pubkey_load(ctx, pk, pubkey);
|
||||
if (sk != NULL) {
|
||||
ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
|
||||
}
|
||||
if (!ret) {
|
||||
*pk = secp256k1_ge_const_g;
|
||||
if (sk != NULL) {
|
||||
*sk = secp256k1_scalar_one;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
|
||||
secp256k1_scalar sk;
|
||||
secp256k1_ge pk;
|
||||
int ret = 0;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(keypair != NULL);
|
||||
memset(keypair, 0, sizeof(*keypair));
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(seckey32 != NULL);
|
||||
|
||||
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
|
||||
secp256k1_keypair_save(keypair, &sk, &pk);
|
||||
secp256k1_memczero(keypair, sizeof(*keypair), !ret);
|
||||
|
||||
secp256k1_scalar_clear(&sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ARG_CHECK(keypair != NULL);
|
||||
|
||||
memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
|
||||
secp256k1_ge pk;
|
||||
int tmp;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ARG_CHECK(keypair != NULL);
|
||||
|
||||
if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
|
||||
return 0;
|
||||
}
|
||||
tmp = secp256k1_extrakeys_ge_even_y(&pk);
|
||||
if (pk_parity != NULL) {
|
||||
*pk_parity = tmp;
|
||||
}
|
||||
secp256k1_xonly_pubkey_save(pubkey, &pk);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) {
|
||||
secp256k1_ge pk;
|
||||
secp256k1_scalar sk;
|
||||
int y_parity;
|
||||
int ret;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(keypair != NULL);
|
||||
ARG_CHECK(tweak32 != NULL);
|
||||
|
||||
ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair);
|
||||
memset(keypair, 0, sizeof(*keypair));
|
||||
|
||||
y_parity = secp256k1_extrakeys_ge_even_y(&pk);
|
||||
if (y_parity == 1) {
|
||||
secp256k1_scalar_negate(&sk, &sk);
|
||||
}
|
||||
|
||||
ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32);
|
||||
ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32);
|
||||
|
||||
secp256k1_declassify(ctx, &ret, sizeof(ret));
|
||||
if (ret) {
|
||||
secp256k1_keypair_save(keypair, &sk, &pk);
|
||||
}
|
||||
|
||||
secp256k1_scalar_clear(&sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
68
src/modules/extrakeys/tests_exhaustive_impl.h
Normal file
68
src/modules/extrakeys/tests_exhaustive_impl.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2020 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
|
||||
#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_
|
||||
|
||||
#include "src/modules/extrakeys/main_impl.h"
|
||||
#include "include/secp256k1_extrakeys.h"
|
||||
|
||||
static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) {
|
||||
secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
secp256k1_pubkey pubkey[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
int parities[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
|
||||
int i;
|
||||
|
||||
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
|
||||
secp256k1_fe fe;
|
||||
secp256k1_scalar scalar_i;
|
||||
unsigned char buf[33];
|
||||
int parity;
|
||||
|
||||
secp256k1_scalar_set_int(&scalar_i, i);
|
||||
secp256k1_scalar_get_b32(buf, &scalar_i);
|
||||
|
||||
/* Construct pubkey and keypair. */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey[i - 1], buf));
|
||||
|
||||
/* Construct serialized xonly_pubkey from keypair. */
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parities[i - 1], &keypair[i - 1]));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
|
||||
|
||||
/* Parse the xonly_pubkey back and verify it matches the previously serialized value. */
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey[i - 1], xonly_pubkey_bytes[i - 1]));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
|
||||
CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
|
||||
|
||||
/* Construct the xonly_pubkey from the pubkey, and verify it matches the same. */
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pubkey[i - 1], &parity, &pubkey[i - 1]));
|
||||
CHECK(parity == parities[i - 1]);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1]));
|
||||
CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0);
|
||||
|
||||
/* Compare the xonly_pubkey bytes against the precomputed group. */
|
||||
secp256k1_fe_set_b32(&fe, xonly_pubkey_bytes[i - 1]);
|
||||
CHECK(secp256k1_fe_equal_var(&fe, &group[i].x));
|
||||
|
||||
/* Check the parity against the precomputed group. */
|
||||
fe = group[i].y;
|
||||
secp256k1_fe_normalize_var(&fe);
|
||||
CHECK(secp256k1_fe_is_odd(&fe) == parities[i - 1]);
|
||||
|
||||
/* Verify that the higher half is identical to the lower half mirrored. */
|
||||
if (i > EXHAUSTIVE_TEST_ORDER / 2) {
|
||||
CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - i - 1], 32) == 0);
|
||||
CHECK(parities[i - 1] == 1 - parities[EXHAUSTIVE_TEST_ORDER - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: keypair/xonly_pubkey tweak tests */
|
||||
}
|
||||
|
||||
#endif
|
||||
524
src/modules/extrakeys/tests_impl.h
Normal file
524
src/modules/extrakeys/tests_impl.h
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2020 Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_
|
||||
#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_
|
||||
|
||||
#include "secp256k1_extrakeys.h"
|
||||
|
||||
static secp256k1_context* api_test_context(int flags, int *ecount) {
|
||||
secp256k1_context *ctx0 = secp256k1_context_create(flags);
|
||||
secp256k1_context_set_error_callback(ctx0, counting_illegal_callback_fn, ecount);
|
||||
secp256k1_context_set_illegal_callback(ctx0, counting_illegal_callback_fn, ecount);
|
||||
return ctx0;
|
||||
}
|
||||
|
||||
void test_xonly_pubkey(void) {
|
||||
secp256k1_pubkey pk;
|
||||
secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
|
||||
secp256k1_ge pk1;
|
||||
secp256k1_ge pk2;
|
||||
secp256k1_fe y;
|
||||
unsigned char sk[32];
|
||||
unsigned char xy_sk[32];
|
||||
unsigned char buf32[32];
|
||||
unsigned char ones32[32];
|
||||
unsigned char zeros64[64] = { 0 };
|
||||
int pk_parity;
|
||||
int i;
|
||||
|
||||
int ecount;
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
|
||||
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
|
||||
|
||||
secp256k1_testrand256(sk);
|
||||
memset(ones32, 0xFF, 32);
|
||||
secp256k1_testrand256(xy_sk);
|
||||
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
|
||||
/* Test xonly_pubkey_from_pubkey */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(sign, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, NULL, &pk_parity, &pk) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
memset(&pk, 0, sizeof(pk));
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
/* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */
|
||||
memset(sk, 0, sizeof(sk));
|
||||
sk[0] = 1;
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&pk, &xonly_pk, sizeof(pk)) == 0);
|
||||
CHECK(pk_parity == 0);
|
||||
|
||||
/* Choose a secret key such that pubkey and xonly_pubkey are each others
|
||||
* negation. */
|
||||
sk[0] = 2;
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, &pk, sizeof(xonly_pk)) != 0);
|
||||
CHECK(pk_parity == 1);
|
||||
secp256k1_pubkey_load(ctx, &pk1, &pk);
|
||||
secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk);
|
||||
CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1);
|
||||
secp256k1_fe_negate(&y, &pk2.y, 1);
|
||||
CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1);
|
||||
|
||||
/* Test xonly_pubkey_serialize and xonly_pubkey_parse */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(none, NULL, &xonly_pk) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, NULL) == 0);
|
||||
CHECK(secp256k1_memcmp_var(buf32, zeros64, 32) == 0);
|
||||
CHECK(ecount == 2);
|
||||
{
|
||||
/* A pubkey filled with 0s will fail to serialize due to pubkey_load
|
||||
* special casing. */
|
||||
secp256k1_xonly_pubkey pk_tmp;
|
||||
memset(&pk_tmp, 0, sizeof(pk_tmp));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk_tmp) == 0);
|
||||
}
|
||||
/* pubkey_load called illegal callback */
|
||||
CHECK(ecount == 3);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &xonly_pk) == 1);
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_parse(none, NULL, buf32) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
|
||||
/* Serialization and parse roundtrip */
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0);
|
||||
|
||||
/* Test parsing invalid field elements */
|
||||
memset(&xonly_pk, 1, sizeof(xonly_pk));
|
||||
/* Overflowing field element */
|
||||
CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, ones32) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
|
||||
memset(&xonly_pk, 1, sizeof(xonly_pk));
|
||||
/* There's no point with x-coordinate 0 on secp256k1 */
|
||||
CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, zeros64) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
|
||||
/* If a random 32-byte string can not be parsed with ec_pubkey_parse
|
||||
* (because interpreted as X coordinate it does not correspond to a point on
|
||||
* the curve) then xonly_pubkey_parse should fail as well. */
|
||||
for (i = 0; i < count; i++) {
|
||||
unsigned char rand33[33];
|
||||
secp256k1_testrand256(&rand33[1]);
|
||||
rand33[0] = SECP256K1_TAG_PUBKEY_EVEN;
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &pk, rand33, 33)) {
|
||||
memset(&xonly_pk, 1, sizeof(xonly_pk));
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
|
||||
} else {
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 1);
|
||||
}
|
||||
}
|
||||
CHECK(ecount == 2);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
void test_xonly_pubkey_tweak(void) {
|
||||
unsigned char zeros64[64] = { 0 };
|
||||
unsigned char overflows[32];
|
||||
unsigned char sk[32];
|
||||
secp256k1_pubkey internal_pk;
|
||||
secp256k1_xonly_pubkey internal_xonly_pk;
|
||||
secp256k1_pubkey output_pk;
|
||||
int pk_parity;
|
||||
unsigned char tweak[32];
|
||||
int i;
|
||||
|
||||
int ecount;
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
|
||||
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
|
||||
|
||||
memset(overflows, 0xff, sizeof(overflows));
|
||||
secp256k1_testrand256(tweak);
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(none, &output_pk, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &output_pk, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, NULL, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, NULL, tweak) == 0);
|
||||
CHECK(ecount == 4);
|
||||
/* NULL internal_xonly_pk zeroes the output_pk */
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
/* NULL tweak zeroes the output_pk */
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
|
||||
/* Invalid tweak zeroes the output_pk */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, overflows) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
|
||||
/* A zero tweak is fine */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, zeros64) == 1);
|
||||
|
||||
/* Fails if the resulting key was infinity */
|
||||
for (i = 0; i < count; i++) {
|
||||
secp256k1_scalar scalar_tweak;
|
||||
/* Because sk may be negated before adding, we need to try with tweak =
|
||||
* sk as well as tweak = -sk. */
|
||||
secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
|
||||
secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
|
||||
secp256k1_scalar_get_b32(tweak, &scalar_tweak);
|
||||
CHECK((secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, sk) == 0)
|
||||
|| (secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0));
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
}
|
||||
|
||||
/* Invalid pk with a valid tweak */
|
||||
memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk));
|
||||
secp256k1_testrand256(tweak);
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
void test_xonly_pubkey_tweak_check(void) {
|
||||
unsigned char zeros64[64] = { 0 };
|
||||
unsigned char overflows[32];
|
||||
unsigned char sk[32];
|
||||
secp256k1_pubkey internal_pk;
|
||||
secp256k1_xonly_pubkey internal_xonly_pk;
|
||||
secp256k1_pubkey output_pk;
|
||||
secp256k1_xonly_pubkey output_xonly_pk;
|
||||
unsigned char output_pk32[32];
|
||||
unsigned char buf32[32];
|
||||
int pk_parity;
|
||||
unsigned char tweak[32];
|
||||
|
||||
int ecount;
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
|
||||
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
|
||||
|
||||
memset(overflows, 0xff, sizeof(overflows));
|
||||
secp256k1_testrand256(tweak);
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &output_xonly_pk, &pk_parity, &output_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(none, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(sign, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, tweak) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, NULL, pk_parity, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
/* invalid pk_parity value */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, 2, &internal_xonly_pk, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, NULL, tweak) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
|
||||
memset(tweak, 1, sizeof(tweak));
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1);
|
||||
|
||||
/* Wrong pk_parity */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0);
|
||||
/* Wrong public key */
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
|
||||
|
||||
/* Overflowing tweak not allowed */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0);
|
||||
CHECK(ecount == 5);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1
|
||||
* additional pubkeys by calling tweak_add. Then verifies every tweak starting
|
||||
* from the last pubkey. */
|
||||
#define N_PUBKEYS 32
|
||||
void test_xonly_pubkey_tweak_recursive(void) {
|
||||
unsigned char sk[32];
|
||||
secp256k1_pubkey pk[N_PUBKEYS];
|
||||
unsigned char pk_serialized[32];
|
||||
unsigned char tweak[N_PUBKEYS - 1][32];
|
||||
int i;
|
||||
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1);
|
||||
/* Add tweaks */
|
||||
for (i = 0; i < N_PUBKEYS - 1; i++) {
|
||||
secp256k1_xonly_pubkey xonly_pk;
|
||||
memset(tweak[i], i + 1, sizeof(tweak[i]));
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1);
|
||||
}
|
||||
|
||||
/* Verify tweaks */
|
||||
for (i = N_PUBKEYS - 1; i > 0; i--) {
|
||||
secp256k1_xonly_pubkey xonly_pk;
|
||||
int pk_parity;
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1);
|
||||
}
|
||||
}
|
||||
#undef N_PUBKEYS
|
||||
|
||||
void test_keypair(void) {
|
||||
unsigned char sk[32];
|
||||
unsigned char zeros96[96] = { 0 };
|
||||
unsigned char overflows[32];
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_pubkey pk, pk_tmp;
|
||||
secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
|
||||
int pk_parity, pk_parity_tmp;
|
||||
int ecount;
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
|
||||
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
|
||||
|
||||
CHECK(sizeof(zeros96) == sizeof(keypair));
|
||||
memset(overflows, 0xFF, sizeof(overflows));
|
||||
|
||||
/* Test keypair_create */
|
||||
ecount = 0;
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
|
||||
CHECK(ecount == 4);
|
||||
|
||||
/* Invalid secret key */
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0);
|
||||
|
||||
/* Test keypair_pub */
|
||||
ecount = 0;
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
|
||||
CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0);
|
||||
|
||||
/* Using an invalid keypair is fine for keypair_pub */
|
||||
memset(&keypair, 0, sizeof(keypair));
|
||||
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0);
|
||||
|
||||
/* keypair holds the same pubkey as pubkey_create */
|
||||
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&pk, &pk_tmp, sizeof(pk)) == 0);
|
||||
|
||||
/** Test keypair_xonly_pub **/
|
||||
ecount = 0;
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
|
||||
/* Using an invalid keypair will set the xonly_pk to 0 (first reset
|
||||
* xonly_pk). */
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
|
||||
memset(&keypair, 0, sizeof(keypair));
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
|
||||
CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
/** keypair holds the same xonly pubkey as pubkey_create **/
|
||||
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
|
||||
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
|
||||
CHECK(pk_parity == pk_parity_tmp);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
void test_keypair_add(void) {
|
||||
unsigned char sk[32];
|
||||
secp256k1_keypair keypair;
|
||||
unsigned char overflows[32];
|
||||
unsigned char zeros96[96] = { 0 };
|
||||
unsigned char tweak[32];
|
||||
int i;
|
||||
int ecount = 0;
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
|
||||
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
|
||||
|
||||
CHECK(sizeof(zeros96) == sizeof(keypair));
|
||||
secp256k1_testrand256(sk);
|
||||
secp256k1_testrand256(tweak);
|
||||
memset(overflows, 0xFF, 32);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
/* This does not set the keypair to zeroes */
|
||||
CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) != 0);
|
||||
|
||||
/* Invalid tweak zeroes the keypair */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0);
|
||||
|
||||
/* A zero tweak is fine */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1);
|
||||
|
||||
/* Fails if the resulting keypair was (sk=0, pk=infinity) */
|
||||
for (i = 0; i < count; i++) {
|
||||
secp256k1_scalar scalar_tweak;
|
||||
secp256k1_keypair keypair_tmp;
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
memcpy(&keypair_tmp, &keypair, sizeof(keypair));
|
||||
/* Because sk may be negated before adding, we need to try with tweak =
|
||||
* sk as well as tweak = -sk. */
|
||||
secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
|
||||
secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
|
||||
secp256k1_scalar_get_b32(tweak, &scalar_tweak);
|
||||
CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0)
|
||||
|| (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0));
|
||||
CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0
|
||||
|| secp256k1_memcmp_var(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0);
|
||||
}
|
||||
|
||||
/* Invalid keypair with a valid tweak */
|
||||
memset(&keypair, 0, sizeof(keypair));
|
||||
secp256k1_testrand256(tweak);
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0);
|
||||
/* Only seckey part of keypair invalid */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
memset(&keypair, 0, 32);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
|
||||
CHECK(ecount == 2);
|
||||
/* Only pubkey part of keypair invalid */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
memset(&keypair.data[32], 0, 64);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
/* Check that the keypair_tweak_add implementation is correct */
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
for (i = 0; i < count; i++) {
|
||||
secp256k1_xonly_pubkey internal_pk;
|
||||
secp256k1_xonly_pubkey output_pk;
|
||||
secp256k1_pubkey output_pk_xy;
|
||||
secp256k1_pubkey output_pk_expected;
|
||||
unsigned char pk32[32];
|
||||
int pk_parity;
|
||||
|
||||
secp256k1_testrand256(tweak);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
|
||||
|
||||
/* Check that it passes xonly_pubkey_tweak_add_check */
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1);
|
||||
|
||||
/* Check that the resulting pubkey matches xonly_pubkey_tweak_add */
|
||||
CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
|
||||
|
||||
/* Check that the secret key in the keypair is tweaked correctly */
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
|
||||
CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
|
||||
}
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
void run_extrakeys_tests(void) {
|
||||
/* xonly key test cases */
|
||||
test_xonly_pubkey();
|
||||
test_xonly_pubkey_tweak();
|
||||
test_xonly_pubkey_tweak_check();
|
||||
test_xonly_pubkey_tweak_recursive();
|
||||
|
||||
/* keypair tests */
|
||||
test_keypair();
|
||||
test_keypair_add();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -175,7 +175,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s
|
|||
secp256k1_scalar blind;
|
||||
secp256k1_scalar_set_b32(&blind, blind32, &overflow);
|
||||
ret = !overflow;
|
||||
CHECK(ret);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &accum, &blind);
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +183,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s
|
|||
secp256k1_sha256_write(&sha256, key32, 32);
|
||||
secp256k1_sha256_finalize(&sha256, b32);
|
||||
ret &= secp256k1_fe_set_b32(&t, b32);
|
||||
CHECK(ret);
|
||||
shallue_van_de_woestijne(&add, &t);
|
||||
if (blind32) {
|
||||
secp256k1_gej_add_ge(&accum, &accum, &add);
|
||||
|
|
@ -197,7 +195,6 @@ static int secp256k1_generator_generate_internal(const secp256k1_context* ctx, s
|
|||
secp256k1_sha256_write(&sha256, key32, 32);
|
||||
secp256k1_sha256_finalize(&sha256, b32);
|
||||
ret &= secp256k1_fe_set_b32(&t, b32);
|
||||
CHECK(ret);
|
||||
shallue_van_de_woestijne(&add, &t);
|
||||
secp256k1_gej_add_ge(&accum, &accum, &add);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ void test_generator_api(void) {
|
|||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_rand256(key);
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(key);
|
||||
secp256k1_testrand256(blind);
|
||||
|
||||
CHECK(secp256k1_generator_generate(none, &gen, key) == 1);
|
||||
CHECK(ecount == 0);
|
||||
|
|
@ -173,7 +173,7 @@ void test_generator_generate(void) {
|
|||
secp256k1_ge_storage ges;
|
||||
int i;
|
||||
unsigned char v[32];
|
||||
static const unsigned char s[32] = {0};
|
||||
unsigned char s[32] = {0};
|
||||
secp256k1_scalar sc;
|
||||
secp256k1_scalar_set_b32(&sc, s, NULL);
|
||||
for (i = 1; i <= 32; i++) {
|
||||
|
|
@ -188,6 +188,14 @@ void test_generator_generate(void) {
|
|||
secp256k1_ge_to_storage(&ges, &ge);
|
||||
CHECK(memcmp(&ges, &results[i - 1], sizeof(secp256k1_ge_storage)) == 0);
|
||||
}
|
||||
|
||||
/* There is no range restriction on the value, but the blinder must be a
|
||||
* valid scalar. Check that an invalid blinder causes the call to fail
|
||||
* but not crash. */
|
||||
memset(v, 0xff, 32);
|
||||
CHECK(secp256k1_generator_generate(ctx, &gen, v));
|
||||
memset(s, 0xff, 32);
|
||||
CHECK(!secp256k1_generator_generate_blinded(ctx, &gen, v, s));
|
||||
}
|
||||
|
||||
void test_generator_fixed_vector(void) {
|
||||
|
|
|
|||
16
src/modules/musig/Makefile.am.include
Normal file
16
src/modules/musig/Makefile.am.include
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
include_HEADERS += include/secp256k1_musig.h
|
||||
noinst_HEADERS += src/modules/musig/main_impl.h
|
||||
noinst_HEADERS += src/modules/musig/tests_impl.h
|
||||
|
||||
noinst_PROGRAMS += example_musig
|
||||
example_musig_SOURCES = src/modules/musig/example.c
|
||||
example_musig_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include $(SECP_INCLUDES)
|
||||
if !ENABLE_COVERAGE
|
||||
example_musig_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
example_musig_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
example_musig_LDFLAGS = -static
|
||||
|
||||
if USE_TESTS
|
||||
TESTS += example_musig
|
||||
endif
|
||||
168
src/modules/musig/example.c
Normal file
168
src/modules/musig/example.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018 Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
/**
|
||||
* This file demonstrates how to use the MuSig module to create a multisignature.
|
||||
* Additionally, see the documentation in include/secp256k1_musig.h.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
#include <secp256k1_musig.h>
|
||||
|
||||
/* Number of public keys involved in creating the aggregate signature */
|
||||
#define N_SIGNERS 3
|
||||
/* Create a key pair and store it in seckey and pubkey */
|
||||
int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k1_xonly_pubkey *pubkey) {
|
||||
int ret;
|
||||
secp256k1_keypair keypair;
|
||||
FILE *frand = fopen("/dev/urandom", "r");
|
||||
if (frand == NULL) {
|
||||
return 0;
|
||||
}
|
||||
do {
|
||||
if(!fread(seckey, 32, 1, frand)) {
|
||||
fclose(frand);
|
||||
return 0;
|
||||
}
|
||||
/* The probability that this not a valid secret key is approximately 2^-128 */
|
||||
} while (!secp256k1_ec_seckey_verify(ctx, seckey));
|
||||
fclose(frand);
|
||||
ret = secp256k1_keypair_create(ctx, &keypair, seckey);
|
||||
ret &= secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, &keypair);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sign a message hash with the given key pairs and store the result in sig */
|
||||
int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) {
|
||||
secp256k1_musig_session musig_session[N_SIGNERS];
|
||||
unsigned char nonce_commitment[N_SIGNERS][32];
|
||||
const unsigned char *nonce_commitment_ptr[N_SIGNERS];
|
||||
secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS];
|
||||
unsigned char nonce[N_SIGNERS][32];
|
||||
int i, j;
|
||||
secp256k1_musig_partial_signature partial_sig[N_SIGNERS];
|
||||
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
FILE *frand;
|
||||
unsigned char session_id32[32];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
|
||||
/* Create combined pubkey and initialize signer data */
|
||||
if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pubkeys, N_SIGNERS)) {
|
||||
return 0;
|
||||
}
|
||||
/* Create random session ID. It is absolutely necessary that the session ID
|
||||
* is unique for every call of secp256k1_musig_session_init. Otherwise
|
||||
* it's trivial for an attacker to extract the secret key! */
|
||||
frand = fopen("/dev/urandom", "r");
|
||||
if(frand == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (!fread(session_id32, 32, 1, frand)) {
|
||||
fclose(frand);
|
||||
return 0;
|
||||
}
|
||||
fclose(frand);
|
||||
/* Initialize session */
|
||||
if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) {
|
||||
return 0;
|
||||
}
|
||||
nonce_commitment_ptr[i] = &nonce_commitment[i][0];
|
||||
}
|
||||
/* Communication round 1: Exchange nonce commitments */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
/* Set nonce commitments in the signer data and get the own public nonce */
|
||||
if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], nonce[i], nonce_commitment_ptr, N_SIGNERS, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Communication round 2: Exchange nonces */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
for (j = 0; j < N_SIGNERS; j++) {
|
||||
if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], nonce[j])) {
|
||||
/* Signer j's nonce does not match the nonce commitment. In this case
|
||||
* abort the protocol. If you make another attempt at finishing the
|
||||
* protocol, create a new session (with a fresh session ID!). */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!secp256k1_musig_session_combine_nonces(ctx, &musig_session[i], signer_data[i], N_SIGNERS, NULL, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
if (!secp256k1_musig_partial_sign(ctx, &musig_session[i], &partial_sig[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Communication round 3: Exchange partial signatures */
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
for (j = 0; j < N_SIGNERS; j++) {
|
||||
/* To check whether signing was successful, it suffices to either verify
|
||||
* the the combined signature with the combined public key using
|
||||
* secp256k1_schnorrsig_verify, or verify all partial signatures of all
|
||||
* signers individually. Verifying the combined signature is cheaper but
|
||||
* verifying the individual partial signatures has the advantage that it
|
||||
* can be used to determine which of the partial signatures are invalid
|
||||
* (if any), i.e., which of the partial signatures cause the combined
|
||||
* signature to be invalid and thus the protocol run to fail. It's also
|
||||
* fine to first verify the combined sig, and only verify the individual
|
||||
* sigs if it does not work.
|
||||
*/
|
||||
if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig64, partial_sig, N_SIGNERS);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
int i;
|
||||
unsigned char seckeys[N_SIGNERS][32];
|
||||
secp256k1_xonly_pubkey pubkeys[N_SIGNERS];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
|
||||
unsigned char sig[64];
|
||||
|
||||
/* Create a context for signing and verification */
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
printf("Creating key pairs......");
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
if (!create_keypair(ctx, seckeys[i], &pubkeys[i])) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Combining public keys...");
|
||||
if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Signing message.........");
|
||||
if (!sign(ctx, seckeys, pubkeys, msg, sig)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Verifying signature.....");
|
||||
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, &combined_pk)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
secp256k1_context_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
702
src/modules/musig/main_impl.h
Normal file
702
src/modules/musig/main_impl.h
Normal file
|
|
@ -0,0 +1,702 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018 Andrew Poelstra, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_MUSIG_MAIN_
|
||||
#define _SECP256K1_MODULE_MUSIG_MAIN_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_musig.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */
|
||||
static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) {
|
||||
secp256k1_sha256 sha;
|
||||
size_t i;
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
for (i = 0; i < np; i++) {
|
||||
unsigned char ser[32];
|
||||
if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_sha256_write(&sha, ser, 32);
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, ell);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */
|
||||
static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
|
||||
sha->s[0] = 0x0fd0690cul;
|
||||
sha->s[1] = 0xfefeae97ul;
|
||||
sha->s[2] = 0x996eac7ful;
|
||||
sha->s[3] = 0x5c30d864ul;
|
||||
sha->s[4] = 0x8c4a0573ul;
|
||||
sha->s[5] = 0xaca1a22ful;
|
||||
sha->s[6] = 0x6f43b801ul;
|
||||
sha->s[7] = 0x85ce27cdul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */
|
||||
static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char buf[32];
|
||||
size_t i;
|
||||
|
||||
secp256k1_musig_sha256_init_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, ell, 32);
|
||||
/* We're hashing the index of the signer instead of its public key as specified
|
||||
* in the MuSig paper. This reduces the total amount of data that needs to be
|
||||
* hashed.
|
||||
* Additionally, it prevents creating identical musig_coefficients for identical
|
||||
* public keys. A participant Bob could choose his public key to be the same as
|
||||
* Alice's, then replay Alice's messages (nonce and partial signature) to create
|
||||
* a valid partial signature. This is not a problem for MuSig per se, but could
|
||||
* result in subtle issues with protocols building on threshold signatures.
|
||||
* With the assumption that public keys are unique, hashing the index is
|
||||
* equivalent to hashing the public key. Because the public key can be
|
||||
* identified by the index given the ordered list of public keys (included in
|
||||
* ell), the index is just a different encoding of the public key.*/
|
||||
for (i = 0; i < sizeof(uint32_t); i++) {
|
||||
unsigned char c = idx;
|
||||
secp256k1_sha256_write(&sha, &c, 1);
|
||||
idx >>= 8;
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_scalar_set_b32(r, buf, NULL);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const secp256k1_context *ctx;
|
||||
unsigned char ell[32];
|
||||
const secp256k1_xonly_pubkey *pks;
|
||||
} secp256k1_musig_pubkey_combine_ecmult_data;
|
||||
|
||||
/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */
|
||||
static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
|
||||
secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data;
|
||||
secp256k1_musig_coefficient(sc, ctx->ell, idx);
|
||||
return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]);
|
||||
}
|
||||
|
||||
static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < n_signers; i++) {
|
||||
memset(&signers[i], 0, sizeof(signers[i]));
|
||||
signers[i].index = i;
|
||||
signers[i].present = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL;
|
||||
|
||||
int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) {
|
||||
secp256k1_musig_pubkey_combine_ecmult_data ecmult_data;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_ge pkp;
|
||||
int pk_parity;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(combined_pk != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(pubkeys != NULL);
|
||||
ARG_CHECK(n_pubkeys > 0);
|
||||
|
||||
ecmult_data.ctx = ctx;
|
||||
ecmult_data.pks = pubkeys;
|
||||
if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(&pkp, &pkj);
|
||||
secp256k1_fe_normalize(&pkp.y);
|
||||
pk_parity = secp256k1_extrakeys_ge_even_y(&pkp);
|
||||
secp256k1_xonly_pubkey_save(combined_pk, &pkp);
|
||||
|
||||
if (pre_session != NULL) {
|
||||
pre_session->magic = pre_session_magic;
|
||||
memcpy(pre_session->pk_hash, ecmult_data.ell, 32);
|
||||
pre_session->pk_parity = pk_parity;
|
||||
pre_session->is_tweaked = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
|
||||
secp256k1_ge pk;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pre_session != NULL);
|
||||
ARG_CHECK(pre_session->magic == pre_session_magic);
|
||||
/* This function can only be called once because otherwise signing would not
|
||||
* succeed */
|
||||
ARG_CHECK(pre_session->is_tweaked == 0);
|
||||
|
||||
pre_session->internal_key_parity = pre_session->pk_parity;
|
||||
if(!secp256k1_xonly_pubkey_tweak_add(ctx, output_pubkey, internal_pubkey, tweak32)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(pre_session->tweak, tweak32, 32);
|
||||
pre_session->is_tweaked = 1;
|
||||
|
||||
if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL;
|
||||
|
||||
int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) {
|
||||
unsigned char combined_ser[32];
|
||||
int overflow;
|
||||
secp256k1_scalar secret;
|
||||
secp256k1_scalar mu;
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_gej pj;
|
||||
secp256k1_ge p;
|
||||
unsigned char nonce_ser[32];
|
||||
size_t nonce_ser_size = sizeof(nonce_ser);
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(signers != NULL);
|
||||
ARG_CHECK(nonce_commitment32 != NULL);
|
||||
ARG_CHECK(session_id32 != NULL);
|
||||
ARG_CHECK(combined_pk != NULL);
|
||||
ARG_CHECK(pre_session != NULL);
|
||||
ARG_CHECK(pre_session->magic == pre_session_magic);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
|
||||
ARG_CHECK(n_signers > 0);
|
||||
ARG_CHECK(n_signers <= UINT32_MAX);
|
||||
ARG_CHECK(my_index < n_signers);
|
||||
|
||||
memset(session, 0, sizeof(*session));
|
||||
|
||||
session->magic = session_magic;
|
||||
if (msg32 != NULL) {
|
||||
memcpy(session->msg, msg32, 32);
|
||||
session->is_msg_set = 1;
|
||||
} else {
|
||||
session->is_msg_set = 0;
|
||||
}
|
||||
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
|
||||
session->pre_session = *pre_session;
|
||||
session->has_secret_data = 1;
|
||||
session->n_signers = (uint32_t) n_signers;
|
||||
secp256k1_musig_signers_init(signers, session->n_signers);
|
||||
|
||||
/* Compute secret key */
|
||||
secp256k1_scalar_set_b32(&secret, seckey, &overflow);
|
||||
if (overflow) {
|
||||
secp256k1_scalar_clear(&secret);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index);
|
||||
/* Compute the signer's public key point and determine if the secret is
|
||||
* negated before signing. That happens if if the signer's pubkey has an odd
|
||||
* Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR
|
||||
* (if tweaked) the internal key has an odd Y coordinate.
|
||||
*
|
||||
* This can be seen by looking at the secret key belonging to `combined_pk`.
|
||||
* Let's define
|
||||
* P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key
|
||||
* point x_i*G, mu_i is the i-th musig coefficient and |.| is a function
|
||||
* that normalizes a point to an even Y by negating if necessary similar to
|
||||
* secp256k1_extrakeys_ge_even_y. Then we have
|
||||
* P := |P'| + t*G where t is the tweak.
|
||||
* And the combined xonly public key is
|
||||
* |P| = x*G
|
||||
* where x = sum_i(b_i*mu_i*x_i) + b'*t
|
||||
* b' = -1 if P != |P|, 1 otherwise
|
||||
* b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1
|
||||
* otherwise.
|
||||
*/
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_fe_normalize(&p.y);
|
||||
if((secp256k1_fe_is_odd(&p.y)
|
||||
+ session->pre_session.pk_parity
|
||||
+ (session->pre_session.is_tweaked
|
||||
&& session->pre_session.internal_key_parity))
|
||||
% 2 == 1) {
|
||||
secp256k1_scalar_negate(&secret, &secret);
|
||||
}
|
||||
secp256k1_scalar_mul(&secret, &secret, &mu);
|
||||
secp256k1_scalar_get_b32(session->seckey, &secret);
|
||||
|
||||
/* Compute secret nonce */
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, session_id32, 32);
|
||||
if (session->is_msg_set) {
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
}
|
||||
secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk);
|
||||
secp256k1_sha256_write(&sha, combined_ser, 32);
|
||||
secp256k1_sha256_write(&sha, seckey, 32);
|
||||
secp256k1_sha256_finalize(&sha, session->secnonce);
|
||||
secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow);
|
||||
if (overflow) {
|
||||
secp256k1_scalar_clear(&secret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compute public nonce and commitment */
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_fe_normalize_var(&p.y);
|
||||
session->partial_nonce_parity = secp256k1_extrakeys_ge_even_y(&p);
|
||||
secp256k1_xonly_pubkey_save(&session->nonce, &p);
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce);
|
||||
secp256k1_sha256_write(&sha, nonce_ser, nonce_ser_size);
|
||||
secp256k1_sha256_finalize(&sha, nonce_commitment32);
|
||||
|
||||
session->round = 0;
|
||||
secp256k1_scalar_clear(&secret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce, const unsigned char *const *commitments, size_t n_commitments, const unsigned char *msg32) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char nonce_commitments_hash[32];
|
||||
size_t i;
|
||||
unsigned char nonce_ser[32];
|
||||
size_t nonce_ser_size = sizeof(nonce_ser);
|
||||
(void) ctx;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(session->magic == session_magic);
|
||||
ARG_CHECK(signers != NULL);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
ARG_CHECK(commitments != NULL);
|
||||
|
||||
ARG_CHECK(session->round == 0);
|
||||
/* If the message was not set during initialization it must be set now. */
|
||||
ARG_CHECK(!(!session->is_msg_set && msg32 == NULL));
|
||||
/* The message can only be set once. */
|
||||
ARG_CHECK(!(session->is_msg_set && msg32 != NULL));
|
||||
ARG_CHECK(session->has_secret_data);
|
||||
ARG_CHECK(n_commitments == session->n_signers);
|
||||
for (i = 0; i < n_commitments; i++) {
|
||||
ARG_CHECK(commitments[i] != NULL);
|
||||
}
|
||||
|
||||
if (msg32 != NULL) {
|
||||
memcpy(session->msg, msg32, 32);
|
||||
session->is_msg_set = 1;
|
||||
}
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
for (i = 0; i < n_commitments; i++) {
|
||||
memcpy(signers[i].nonce_commitment, commitments[i], 32);
|
||||
secp256k1_sha256_write(&sha, commitments[i], 32);
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
|
||||
memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32);
|
||||
|
||||
secp256k1_xonly_pubkey_serialize(ctx, nonce_ser, &session->nonce);
|
||||
memcpy(nonce, &nonce_ser, nonce_ser_size);
|
||||
session->round = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_session_init_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) {
|
||||
size_t i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(signers != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(combined_pk != NULL);
|
||||
ARG_CHECK(pre_session != NULL);
|
||||
ARG_CHECK(pre_session->magic == pre_session_magic);
|
||||
ARG_CHECK(commitments != NULL);
|
||||
/* Check n_signers before checking commitments to allow testing the case where
|
||||
* n_signers is big without allocating the space. */
|
||||
ARG_CHECK(n_signers > 0);
|
||||
ARG_CHECK(n_signers <= UINT32_MAX);
|
||||
for (i = 0; i < n_signers; i++) {
|
||||
ARG_CHECK(commitments[i] != NULL);
|
||||
}
|
||||
(void) ctx;
|
||||
|
||||
memset(session, 0, sizeof(*session));
|
||||
|
||||
session->magic = session_magic;
|
||||
memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk));
|
||||
session->pre_session = *pre_session;
|
||||
session->n_signers = (uint32_t) n_signers;
|
||||
secp256k1_musig_signers_init(signers, session->n_signers);
|
||||
|
||||
session->pre_session = *pre_session;
|
||||
session->is_msg_set = 1;
|
||||
memcpy(session->msg, msg32, 32);
|
||||
session->has_secret_data = 0;
|
||||
|
||||
for (i = 0; i < n_signers; i++) {
|
||||
memcpy(signers[i].nonce_commitment, commitments[i], 32);
|
||||
}
|
||||
session->round = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const unsigned char *nonce) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char commit[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(signer != NULL);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, nonce, 32);
|
||||
secp256k1_sha256_finalize(&sha, commit);
|
||||
|
||||
if (memcmp(commit, signer->nonce_commitment, 32) != 0) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&signer->nonce, nonce, sizeof(*nonce));
|
||||
if (!secp256k1_xonly_pubkey_parse(ctx, &signer->nonce, nonce)) {
|
||||
return 0;
|
||||
}
|
||||
signer->present = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_parity, const secp256k1_pubkey *adaptor) {
|
||||
secp256k1_gej combined_noncej;
|
||||
secp256k1_ge combined_noncep;
|
||||
secp256k1_ge noncep;
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char nonce_commitments_hash[32];
|
||||
size_t i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(signers != NULL);
|
||||
ARG_CHECK(session->magic == session_magic);
|
||||
ARG_CHECK(session->round == 1);
|
||||
ARG_CHECK(n_signers == session->n_signers);
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_gej_set_infinity(&combined_noncej);
|
||||
for (i = 0; i < n_signers; i++) {
|
||||
if (!signers[i].present) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32);
|
||||
secp256k1_xonly_pubkey_load(ctx, &noncep, &signers[i].nonce);
|
||||
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, nonce_commitments_hash);
|
||||
/* If the signers' commitments changed between get_public_nonce and now we
|
||||
* have to abort because in that case they may have seen our nonce before
|
||||
* creating their commitment. That can happen if the signer_data given to
|
||||
* this function is different to the signer_data given to get_public_nonce.
|
||||
* */
|
||||
if (session->has_secret_data
|
||||
&& memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add public adaptor to nonce */
|
||||
if (adaptor != NULL) {
|
||||
secp256k1_pubkey_load(ctx, &noncep, adaptor);
|
||||
secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL);
|
||||
}
|
||||
|
||||
/* Negate nonce if Y coordinate is not square */
|
||||
secp256k1_ge_set_gej(&combined_noncep, &combined_noncej);
|
||||
secp256k1_fe_normalize_var(&combined_noncep.y);
|
||||
session->combined_nonce_parity = secp256k1_extrakeys_ge_even_y(&combined_noncep);
|
||||
if (nonce_parity != NULL) {
|
||||
*nonce_parity = session->combined_nonce_parity;
|
||||
}
|
||||
secp256k1_xonly_pubkey_save(&session->combined_nonce, &combined_noncep);
|
||||
session->round = 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(out32 != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
memcpy(out32, sig->data, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(in32 != NULL);
|
||||
memcpy(sig->data, in32, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */
|
||||
static void secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) {
|
||||
unsigned char buf[32];
|
||||
secp256k1_ge rp;
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
VERIFY_CHECK(session->round >= 2);
|
||||
|
||||
secp256k1_schnorrsig_sha256_tagged(&sha);
|
||||
secp256k1_xonly_pubkey_load(ctx, &rp, &session->combined_nonce);
|
||||
secp256k1_fe_get_b32(buf, &rp.x);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
|
||||
secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
secp256k1_sha256_write(&sha, session->msg, 32);
|
||||
secp256k1_sha256_finalize(&sha, msghash);
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) {
|
||||
unsigned char msghash[32];
|
||||
int overflow;
|
||||
secp256k1_scalar sk;
|
||||
secp256k1_scalar e, k;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(partial_sig != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(session->magic == session_magic);
|
||||
ARG_CHECK(session->round == 2);
|
||||
ARG_CHECK(session->has_secret_data);
|
||||
|
||||
/* build message hash */
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash, session);
|
||||
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&sk, session->seckey, &overflow);
|
||||
if (overflow) {
|
||||
secp256k1_scalar_clear(&sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&k, session->secnonce, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&k)) {
|
||||
secp256k1_scalar_clear(&sk);
|
||||
secp256k1_scalar_clear(&k);
|
||||
return 0;
|
||||
}
|
||||
if (session->partial_nonce_parity != session->combined_nonce_parity) {
|
||||
secp256k1_scalar_negate(&k, &k);
|
||||
}
|
||||
|
||||
/* Sign */
|
||||
secp256k1_scalar_mul(&e, &e, &sk);
|
||||
secp256k1_scalar_add(&e, &e, &k);
|
||||
secp256k1_scalar_get_b32(&partial_sig->data[0], &e);
|
||||
secp256k1_scalar_clear(&sk);
|
||||
secp256k1_scalar_clear(&k);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) {
|
||||
size_t i;
|
||||
secp256k1_scalar s;
|
||||
secp256k1_ge noncep;
|
||||
(void) ctx;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(partial_sigs != NULL);
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(session->magic == session_magic);
|
||||
ARG_CHECK(session->round == 2);
|
||||
|
||||
if (n_sigs != session->n_signers) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_clear(&s);
|
||||
for (i = 0; i < n_sigs; i++) {
|
||||
int overflow;
|
||||
secp256k1_scalar term;
|
||||
|
||||
secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_add(&s, &s, &term);
|
||||
}
|
||||
|
||||
/* If there is a tweak then add (or subtract) `msghash` times `tweak` to `s`.*/
|
||||
if (session->pre_session.is_tweaked) {
|
||||
unsigned char msghash[32];
|
||||
secp256k1_scalar e, scalar_tweak;
|
||||
int overflow = 0;
|
||||
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash, session);
|
||||
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
||||
secp256k1_scalar_set_b32(&scalar_tweak, session->pre_session.tweak, &overflow);
|
||||
if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) {
|
||||
/* This mimics the behavior of secp256k1_ec_seckey_tweak_mul regarding
|
||||
* overflow and tweak being 0. */
|
||||
return 0;
|
||||
}
|
||||
if (session->pre_session.pk_parity) {
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
}
|
||||
secp256k1_scalar_add(&s, &s, &e);
|
||||
}
|
||||
|
||||
secp256k1_xonly_pubkey_load(ctx, &noncep, &session->combined_nonce);
|
||||
VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y));
|
||||
secp256k1_fe_normalize(&noncep.x);
|
||||
secp256k1_fe_get_b32(&sig64[0], &noncep.x);
|
||||
secp256k1_scalar_get_b32(&sig64[32], &s);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_xonly_pubkey *pubkey) {
|
||||
unsigned char msghash[32];
|
||||
secp256k1_scalar s;
|
||||
secp256k1_scalar e;
|
||||
secp256k1_scalar mu;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge pkp;
|
||||
secp256k1_ge rp;
|
||||
int overflow;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(session != NULL);
|
||||
ARG_CHECK(signer != NULL);
|
||||
ARG_CHECK(partial_sig != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
ARG_CHECK(session->magic == session_magic);
|
||||
ARG_CHECK(session->round == 2);
|
||||
ARG_CHECK(signer->present);
|
||||
|
||||
secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash, session);
|
||||
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
||||
|
||||
/* Multiplying the messagehash by the musig coefficient is equivalent
|
||||
* to multiplying the signer's public key by the coefficient, except
|
||||
* much easier to do. */
|
||||
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index);
|
||||
secp256k1_scalar_mul(&e, &e, &mu);
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the MuSig-combined point has an odd Y coordinate, the signers will
|
||||
* sign for the negation of their individual xonly public key such that the
|
||||
* combined signature is valid for the MuSig aggregated xonly key. If the
|
||||
* MuSig-combined point was tweaked then `e` is negated if the combined key
|
||||
* has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/
|
||||
if (session->pre_session.pk_parity
|
||||
!= (session->pre_session.is_tweaked
|
||||
&& session->pre_session.internal_key_parity)) {
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
}
|
||||
|
||||
/* Compute rj = s*G + (-e)*pkj */
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&pkj, &pkp);
|
||||
secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
|
||||
|
||||
if (!session->combined_nonce_parity) {
|
||||
secp256k1_ge_neg(&rp, &rp);
|
||||
}
|
||||
secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL);
|
||||
|
||||
return secp256k1_gej_is_infinity(&rj);
|
||||
}
|
||||
|
||||
int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_parity) {
|
||||
secp256k1_scalar s;
|
||||
secp256k1_scalar t;
|
||||
int overflow;
|
||||
|
||||
(void) ctx;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(adaptor_sig != NULL);
|
||||
ARG_CHECK(partial_sig != NULL);
|
||||
ARG_CHECK(sec_adaptor32 != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow);
|
||||
if (overflow) {
|
||||
secp256k1_scalar_clear(&t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nonce_parity) {
|
||||
secp256k1_scalar_negate(&t, &t);
|
||||
}
|
||||
|
||||
secp256k1_scalar_add(&s, &s, &t);
|
||||
secp256k1_scalar_get_b32(adaptor_sig->data, &s);
|
||||
secp256k1_scalar_clear(&t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_parity) {
|
||||
secp256k1_scalar t;
|
||||
secp256k1_scalar s;
|
||||
int overflow;
|
||||
size_t i;
|
||||
|
||||
(void) ctx;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sec_adaptor32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(partial_sigs != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&t, &sig64[32], &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_negate(&t, &t);
|
||||
|
||||
for (i = 0; i < n_partial_sigs; i++) {
|
||||
secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow);
|
||||
if (overflow) {
|
||||
secp256k1_scalar_clear(&t);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_add(&t, &t, &s);
|
||||
}
|
||||
|
||||
if (!nonce_parity) {
|
||||
secp256k1_scalar_negate(&t, &t);
|
||||
}
|
||||
secp256k1_scalar_get_b32(sec_adaptor32, &t);
|
||||
secp256k1_scalar_clear(&t);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
198
src/modules/musig/musig.md
Normal file
198
src/modules/musig/musig.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
MuSig - Rogue-Key-Resistant Multisignatures Module
|
||||
===========================
|
||||
|
||||
This module implements the MuSig [1] multisignature scheme. The majority of
|
||||
the module is an API designed to be used by signing or auditing participants
|
||||
in a multisignature scheme. This involves a somewhat complex state machine
|
||||
and significant effort has been taken to prevent accidental misuse of the
|
||||
API in ways that could lead to accidental signatures or loss of key material.
|
||||
|
||||
The resulting signatures are valid Schnorr signatures as described in [2].
|
||||
|
||||
# Theory
|
||||
|
||||
In MuSig all signers contribute key material to a single signing key,
|
||||
using the equation
|
||||
|
||||
P = sum_i µ_i * P_i
|
||||
|
||||
where `P_i` is the public key of the `i`th signer and `µ_i` is a so-called
|
||||
_MuSig coefficient_ computed according to the following equation
|
||||
|
||||
L = H(P_1 || P_2 || ... || P_n)
|
||||
µ_i = H(L || i)
|
||||
|
||||
where H is a hash function modelled as a random oracle.
|
||||
|
||||
To produce a multisignature `(s, R)` on a message `m` using verification key
|
||||
`P`, signers act as follows:
|
||||
|
||||
1. Each computes a nonce, or ephemeral keypair, `(k_i, R_i)`. Every signer
|
||||
communicates `H(R_i)` to every participant (both signers and auditors).
|
||||
2. Upon receipt of every `H(R_i)`, each signer communicates `R_i` to every
|
||||
participant. The recipients check that each `R_i` is consistent with the
|
||||
previously-communicated hash.
|
||||
3. Each signer computes a combined nonce
|
||||
`R = sum_i R_i`
|
||||
and shared challenge
|
||||
`e = H(R || P || m)`
|
||||
and partial signature
|
||||
`s_i = k_i + µ_i*x_i*e`
|
||||
where `x_i` is the secret key corresponding to `P_i`.
|
||||
|
||||
The complete signature is then the `(s, R)` where `s = sum_i s_i` and `R = sum_i R_i`.
|
||||
|
||||
# API Usage
|
||||
|
||||
The following sections describe use of our API, and are mirrored in code in `src/modules/musig/example.c`.
|
||||
|
||||
It is essential to security that signers use a unique uniformly random nonce for all
|
||||
signing sessions, and that they do not reuse these nonces even in the case that a
|
||||
signing session fails to complete. To that end, all signing state is encapsulated
|
||||
in the data structure `secp256k1_musig_session`. The API does not expose any
|
||||
functionality to serialize or deserialize this structure; it is designed to exist
|
||||
only in memory.
|
||||
|
||||
Users who need to persist this structure must take additional security measures
|
||||
which cannot be enforced by a C API. Some guidance is provided in the documentation
|
||||
for this data structure in `include/secp256k1_musig.h`.
|
||||
|
||||
## Key Generation
|
||||
|
||||
To use MuSig, users must first compute their combined public key `P`, which is
|
||||
suitable for use on a blockchain or other public key repository. They do this
|
||||
by calling `secp256k1_musig_pubkey_combine`.
|
||||
|
||||
This function takes as input a list of public keys `P_i` in the argument
|
||||
`pubkeys`. It outputs the combined public key `P` in the out-pointer `combined_pk`
|
||||
and hash `L` in the out-pointer `pk_hash32`, if this pointer is non-NULL.
|
||||
|
||||
## Signing
|
||||
|
||||
A participant who wishes to sign a message (as opposed to observing/auditing the
|
||||
signature process, which is also a supported mode) acts as follows.
|
||||
|
||||
### Signing Participant
|
||||
|
||||
1. The signer starts the session by calling `secp256k1_musig_session_init`.
|
||||
This function outputs
|
||||
- an initialized session state in the out-pointer `session`
|
||||
- an array of initialized signer data in the out-pointer `signers`
|
||||
- a commitment `H(R_i)` to a nonce in the out-pointer `nonce_commitment32`
|
||||
It takes as input
|
||||
- a unique session ID `session_id32`
|
||||
- (optionally) a message to be signed `msg32`
|
||||
- the combined public key output from `secp256k1_musig_pubkey_combine`
|
||||
- the public key hash output from `secp256k1_musig_pubkey_combine`
|
||||
- the signer's index `i` `my_index`
|
||||
- the signer's secret key `seckey`
|
||||
2. The signer then communicates `H(R_i)` to all other signers, and receives
|
||||
commitments `H(R_j)` from all other signers `j`. These hashes are simply
|
||||
length-32 byte arrays which can be communicated however is communicated.
|
||||
3. Once all signers nonce commitments have been received, the signer records
|
||||
these commitments with the function `secp256k1_musig_session_get_public_nonce`.
|
||||
If the signer did not provide a message to `secp256k1_musig_session_init`,
|
||||
a message must be provided now.
|
||||
This function updates in place
|
||||
- the session state `session`
|
||||
- the array of signer data `signers`
|
||||
taking in as input the list of commitments `commitments` and outputting the
|
||||
signer's public nonce `R_i` in the out-pointer `nonce`.
|
||||
4. The signer then communicates `R_i` to all other signers, and receives `R_j`
|
||||
from each signer `j`. On receipt of a nonce `R_j` he calls the function
|
||||
`secp256k1_musig_set_nonce` to record this fact. This function checks that
|
||||
the received nonce is consistent with the previously-received nonce and will
|
||||
return 0 in this case. The signer must also call this function with his own
|
||||
nonce and his own index `i`.
|
||||
These nonces `R_i` are secp256k1 public keys; they should be serialized using
|
||||
`secp256k1_ec_pubkey_serialize` and parsed with `secp256k1_ec_pubkey_parse`.
|
||||
5. Once all nonces have been exchanged in this way, signers are able to compute
|
||||
their partial signatures. They do so by calling `secp256k1_musig_session_combine_nonces`
|
||||
which updates in place
|
||||
- the session state `session`
|
||||
- the array of signer data `signers`
|
||||
It outputs an auxiliary integer `nonce_is_negated` and has an auxiliary input
|
||||
`adaptor`. Both of these may be set to NULL for ordinary signing purposes.
|
||||
6. The signer computes a partial signature `s_i` using the function
|
||||
`secp256k1_musig_partial_sign` which takes the session state as input and
|
||||
partial signature as output.
|
||||
7. The signer then communicates the partial signature `s_i` to all other signers, or
|
||||
to a central coordinator. These partial signatures should be serialized using
|
||||
`musig_partial_signature_serialize` and parsed using `musig_partial_signature_parse`.
|
||||
8. Each signer calls `secp256k1_musig_partial_sig_verify` on the other signers' partial
|
||||
signatures to verify their correctness. If only the validity of the final signature
|
||||
is important, not assigning blame, this step can be skipped.
|
||||
9. Any signer, or central coordinator, may combine the partial signatures to obtain
|
||||
a complete signature using `secp256k1_musig_partial_sig_combine`. This function takes
|
||||
a signing session and array of MuSig partial signatures, and outputs a single
|
||||
Schnorr signature.
|
||||
|
||||
### Non-signing Participant
|
||||
|
||||
A participant who wants to verify the signing process, i.e. check that nonce commitments
|
||||
are consistent and partial signatures are correct without contributing a partial signature,
|
||||
may do so using the above instructions except for the following changes:
|
||||
|
||||
1. A signing session should be produced using `musig_session_init_verifier`
|
||||
rather than `musig_session_init`; this function takes no secret data or
|
||||
signer index.
|
||||
2. The participant receives nonce commitments, public nonces and partial signatures,
|
||||
but does not produce these values. Therefore `secp256k1_musig_session_get_public_nonce`
|
||||
and `secp256k1_musig_partial_sign` are not called.
|
||||
|
||||
### Verifier
|
||||
|
||||
The final signature is simply a valid Schnorr signature using the combined public key. It
|
||||
can be verified using the `secp256k1_schnorrsig_verify` with the correct message and
|
||||
public key output from `secp256k1_musig_pubkey_combine`.
|
||||
|
||||
## Atomic Swaps
|
||||
|
||||
The signing API supports the production of "adaptor signatures", modified partial signatures
|
||||
which are offset by an auxiliary secret known to one party. That is,
|
||||
1. One party generates a (secret) adaptor `t` with corresponding (public) adaptor `T = t*G`.
|
||||
2. When combining nonces, each party adds `T` to the total nonce used in the signature.
|
||||
3. The party who knows `t` must "adapt" their partial signature with `t` to complete the
|
||||
signature.
|
||||
4. Any party who sees both the final signature and the original partial signatures
|
||||
can compute `t`.
|
||||
|
||||
Using these adaptor signatures, two 2-of-2 MuSig signing protocols can be executed in
|
||||
parallel such that one party's partial signatures are made atomic. That is, when the other
|
||||
party learns one partial signature, she automatically learns the other. This has applications
|
||||
in cross-chain atomic swaps.
|
||||
|
||||
Such a protocol can be executed as follows. Consider two participants, Alice and Bob, who
|
||||
are simultaneously producing 2-of-2 multisignatures for two blockchains A and B. They act
|
||||
as follows.
|
||||
|
||||
1. Before the protocol begins, Bob chooses a 32-byte auxiliary secret `t` at random and
|
||||
computes a corresponding public point `T` by calling `secp256k1_ec_pubkey_create`.
|
||||
He communicates `T` to Alice.
|
||||
2. Together, the parties execute steps 1-4 of the signing protocol above.
|
||||
3. At step 5, when combining the two parties' public nonces, both parties call
|
||||
`secp256k1_musig_session_combine_nonces` with `adaptor` set to `T` and `nonce_is_negated`
|
||||
set to a non-NULL pointer to int.
|
||||
4. Steps 6 and 7 proceed as before. Step 8, verifying the partial signatures, is now
|
||||
essential to the security of the protocol and must not be omitted!
|
||||
|
||||
The above steps are executed identically for both signing sessions. However, step 9 will
|
||||
not work as before, since the partial signatures will not add up to a valid total signature.
|
||||
Additional steps must be taken, and it is at this point that the two signing sessions
|
||||
diverge. From here on we consider "Session A" which benefits Alice (e.g. which sends her
|
||||
coins) and "Session B" which benefits Bob (e.g. which sends him coins).
|
||||
|
||||
5. In Session B, Bob calls `secp256k1_musig_partial_sig_adapt` with his partial signature
|
||||
and `t`, to produce an adaptor signature. He can then call `secp256k1_musig_partial_sig_combine`
|
||||
with this adaptor signature and Alice's partial signature, to produce a complete
|
||||
signature for blockchain B.
|
||||
6. Alice reads this signature from blockchain B. She calls `secp256k1_musig_extract_secret_adaptor`,
|
||||
passing the complete signature along with her and Bob's partial signatures from Session B.
|
||||
This function outputs `t`, which until this point was only known to Bob.
|
||||
7. In Session A, Alice is now able to replicate Bob's action, calling
|
||||
`secp256k1_musig_partial_sig_adapt` with her own partial signature and `t`, ultimately
|
||||
producing a complete signature on blockchain A.
|
||||
|
||||
[1] https://eprint.iacr.org/2018/068
|
||||
[2] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
|
||||
|
||||
969
src/modules/musig/tests_impl.h
Normal file
969
src/modules/musig/tests_impl.h
Normal file
|
|
@ -0,0 +1,969 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018 Andrew Poelstra *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_MUSIG_TESTS_
|
||||
#define _SECP256K1_MODULE_MUSIG_TESTS_
|
||||
|
||||
#include "secp256k1_musig.h"
|
||||
|
||||
int secp256k1_xonly_pubkey_create(secp256k1_xonly_pubkey *pk, const unsigned char *seckey) {
|
||||
int ret;
|
||||
secp256k1_keypair keypair;
|
||||
ret = secp256k1_keypair_create(ctx, &keypair, seckey);
|
||||
ret &= secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig combine, sign, verify
|
||||
* test. */
|
||||
void musig_simple_test(secp256k1_scratch_space *scratch) {
|
||||
unsigned char sk[2][32];
|
||||
secp256k1_musig_session session[2];
|
||||
secp256k1_musig_session_signer_data signer0[2];
|
||||
secp256k1_musig_session_signer_data signer1[2];
|
||||
unsigned char nonce_commitment[2][32];
|
||||
unsigned char msg[32];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
unsigned char session_id[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
const unsigned char *ncs[2];
|
||||
unsigned char public_nonce[3][32];
|
||||
secp256k1_musig_partial_signature partial_sig[2];
|
||||
unsigned char final_sig[64];
|
||||
|
||||
secp256k1_testrand256(session_id[0]);
|
||||
secp256k1_testrand256(session_id[1]);
|
||||
secp256k1_testrand256(sk[0]);
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], public_nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], public_nonce[1]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], public_nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], public_nonce[1]) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, &combined_pk) == 1);
|
||||
}
|
||||
|
||||
void musig_api_tests(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_scratch_space *scratch_small;
|
||||
secp256k1_musig_session session[2];
|
||||
secp256k1_musig_session session_uninitialized;
|
||||
secp256k1_musig_session verifier_session;
|
||||
secp256k1_musig_session_signer_data signer0[2];
|
||||
secp256k1_musig_session_signer_data signer1[2];
|
||||
secp256k1_musig_session_signer_data verifier_signer_data[2];
|
||||
secp256k1_musig_partial_signature partial_sig[2];
|
||||
secp256k1_musig_partial_signature partial_sig_adapted[2];
|
||||
secp256k1_musig_partial_signature partial_sig_overflow;
|
||||
unsigned char final_sig[64];
|
||||
unsigned char final_sig_cmp[64];
|
||||
|
||||
unsigned char buf[32];
|
||||
unsigned char sk[2][32];
|
||||
unsigned char ones[32];
|
||||
unsigned char session_id[2][32];
|
||||
unsigned char nonce_commitment[2][32];
|
||||
int combined_nonce_parity;
|
||||
const unsigned char *ncs[2];
|
||||
unsigned char msg[32];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
secp256k1_musig_pre_session pre_session_uninitialized;
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
unsigned char tweak[32];
|
||||
|
||||
unsigned char sec_adaptor[32];
|
||||
unsigned char sec_adaptor1[32];
|
||||
secp256k1_pubkey adaptor;
|
||||
|
||||
/** setup **/
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
int ecount;
|
||||
|
||||
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
|
||||
memset(ones, 0xff, 32);
|
||||
/* Simulate structs being uninitialized by setting it to 0s. We don't want
|
||||
* to produce undefined behavior by actually providing uninitialized
|
||||
* structs. */
|
||||
memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized));
|
||||
memset(&session_uninitialized, 0, sizeof(session_uninitialized));
|
||||
|
||||
secp256k1_testrand256(session_id[0]);
|
||||
secp256k1_testrand256(session_id[1]);
|
||||
secp256k1_testrand256(sk[0]);
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
secp256k1_testrand256(sec_adaptor);
|
||||
secp256k1_testrand256(tweak);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1);
|
||||
|
||||
|
||||
/** main test body **/
|
||||
|
||||
/* Key combination */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(ecount == 2);
|
||||
/* pubkey_combine does not require a scratch space */
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(ecount == 2);
|
||||
/* A small scratch space works too, but will result in using an ineffecient algorithm */
|
||||
scratch_small = secp256k1_scratch_space_create(ctx, 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
secp256k1_scratch_space_destroy(ctx, scratch_small);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
|
||||
/** Tweaking */
|
||||
ecount = 0;
|
||||
{
|
||||
secp256k1_xonly_pubkey tmp_internal_pk = combined_pk;
|
||||
secp256k1_pubkey tmp_output_pk;
|
||||
secp256k1_musig_pre_session tmp_pre_session = pre_session;
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
|
||||
/* Reset pre_session */
|
||||
tmp_pre_session = pre_session;
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
|
||||
CHECK(ecount == 2);
|
||||
tmp_pre_session = pre_session;
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 3);
|
||||
/* Uninitialized pre_session */
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &pre_session_uninitialized, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 4);
|
||||
/* Using the same pre_session twice does not work */
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 5);
|
||||
tmp_pre_session = pre_session;
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, NULL, &tmp_internal_pk, tweak) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, NULL, tweak) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, NULL) == 0);
|
||||
CHECK(ecount == 8);
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_pre_session, &tmp_output_pk, &tmp_internal_pk, ones) == 0);
|
||||
CHECK(ecount == 8);
|
||||
}
|
||||
|
||||
/** Session creation **/
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 8);
|
||||
/* Uninitialized pre_session */
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 9);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 10);
|
||||
/* If more than UINT32_MAX fits in a size_t, test that session_init
|
||||
* rejects n_signers that high. */
|
||||
if (SIZE_MAX > UINT32_MAX) {
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0);
|
||||
}
|
||||
CHECK(ecount == 11);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0);
|
||||
CHECK(ecount == 12);
|
||||
/* secret key overflows */
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0);
|
||||
CHECK(ecount == 12);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1);
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0);
|
||||
CHECK(ecount == 6);
|
||||
if (SIZE_MAX > UINT32_MAX) {
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0);
|
||||
}
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_session_init_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1);
|
||||
|
||||
/** Signing step 0 -- exchange nonce commitments */
|
||||
ecount = 0;
|
||||
{
|
||||
unsigned char nonce[32];
|
||||
secp256k1_musig_session session_0_tmp;
|
||||
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
|
||||
/* Can obtain public nonce after commitments have been exchanged; still can't sign */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, nonce, ncs, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session_0_tmp, &partial_sig[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
}
|
||||
|
||||
/** Signing step 1 -- exchange nonces */
|
||||
ecount = 0;
|
||||
{
|
||||
unsigned char public_nonce[3][32];
|
||||
secp256k1_musig_session session_0_tmp;
|
||||
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 2, NULL) == 1);
|
||||
CHECK(ecount == 0);
|
||||
/* Reset session */
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, public_nonce[0], ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
/* uninitialized session */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_uninitialized, signer0, public_nonce[0], ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, NULL, public_nonce[0], ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, NULL, ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], NULL, 2, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
/* Number of commitments and number of signers are different */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session_0_tmp, signer0, public_nonce[0], ncs, 1, NULL) == 0);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, public_nonce[0], ncs, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, public_nonce[1], ncs, 2, NULL) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer0[0], public_nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[0]) == 0);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer0[1], public_nonce[1]) == 1);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
CHECK(secp256k1_musig_set_nonce(none, NULL, public_nonce[0]) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0);
|
||||
CHECK(ecount == 8);
|
||||
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer1[0], public_nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &signer1[1], public_nonce[1]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], public_nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], public_nonce[1]) == 1);
|
||||
|
||||
ecount = 0;
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, &adaptor) == 1);
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &combined_nonce_parity, &adaptor) == 0);
|
||||
CHECK(ecount == 1);
|
||||
/* Uninitialized session */
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_uninitialized, signer0, 2, &combined_nonce_parity, &adaptor) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, NULL, 2, &combined_nonce_parity, &adaptor) == 0);
|
||||
CHECK(ecount == 3);
|
||||
/* Number of signers differs from number during intialization */
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 1, &combined_nonce_parity, &adaptor) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, NULL, &adaptor) == 1);
|
||||
CHECK(ecount == 4);
|
||||
memcpy(&session_0_tmp, &session[0], sizeof(session_0_tmp));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session_0_tmp, signer0, 2, &combined_nonce_parity, NULL) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &combined_nonce_parity, &adaptor) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &combined_nonce_parity, &adaptor) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &combined_nonce_parity, &adaptor) == 1);
|
||||
}
|
||||
|
||||
/** Signing step 2 -- partial signatures */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1);
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
/* Uninitialized session */
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session_uninitialized, &partial_sig[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1);
|
||||
/* observer can't sign */
|
||||
CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0);
|
||||
CHECK(ecount == 4);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_signature_serialize(none, NULL, &partial_sig[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_partial_signature_serialize(none, buf, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], buf) == 1);
|
||||
CHECK(secp256k1_musig_partial_signature_parse(none, NULL, buf) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig_overflow, ones) == 1);
|
||||
|
||||
/** Partial signature verification */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_sig_verify(none, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(sign, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[1], &pk[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 3);
|
||||
/* Unitialized session */
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session_uninitialized, &signer0[0], &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0);
|
||||
CHECK(ecount == 7);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1);
|
||||
CHECK(ecount == 7);
|
||||
|
||||
/** Adaptor signature verification */
|
||||
memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1]));
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, combined_nonce_parity) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, combined_nonce_parity) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, combined_nonce_parity) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
/** Signing combining and verification */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1);
|
||||
CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1);
|
||||
CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0);
|
||||
CHECK(ecount == 1);
|
||||
/* Unitialized session */
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session_uninitialized, final_sig, partial_sig_adapted, 2) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0);
|
||||
CHECK(ecount == 4);
|
||||
{
|
||||
secp256k1_musig_partial_signature partial_sig_tmp[2];
|
||||
partial_sig_tmp[0] = partial_sig_adapted[0];
|
||||
partial_sig_tmp[1] = partial_sig_overflow;
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0);
|
||||
}
|
||||
CHECK(ecount == 4);
|
||||
/* Wrong number of partial sigs */
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1);
|
||||
CHECK(ecount == 4);
|
||||
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1);
|
||||
|
||||
/** Secret adaptor can be extracted from signature */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, combined_nonce_parity) == 1);
|
||||
CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0);
|
||||
CHECK(ecount == 2);
|
||||
{
|
||||
unsigned char final_sig_tmp[64];
|
||||
memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp));
|
||||
memcpy(&final_sig_tmp[32], ones, 32);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, combined_nonce_parity) == 0);
|
||||
}
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0);
|
||||
CHECK(ecount == 3);
|
||||
{
|
||||
secp256k1_musig_partial_signature partial_sig_tmp[2];
|
||||
partial_sig_tmp[0] = partial_sig[0];
|
||||
partial_sig_tmp[1] = partial_sig_overflow;
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, combined_nonce_parity) == 0);
|
||||
}
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1);
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, 1) == 1);
|
||||
|
||||
/** cleanup **/
|
||||
memset(&session, 0, sizeof(session));
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(vrfy);
|
||||
}
|
||||
|
||||
/* Initializes two sessions, one use the given parameters (session_id,
|
||||
* nonce_commitments, etc.) except that `session_tmp` uses new signers with different
|
||||
* public keys. The point of this test is to call `musig_session_get_public_nonce`
|
||||
* with signers from `session_tmp` who have different public keys than the correct
|
||||
* ones and return the resulting messagehash. This should not result in a different
|
||||
* messagehash because the public keys of the signers are only used during session
|
||||
* initialization. */
|
||||
void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id) {
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_session session_tmp;
|
||||
unsigned char nonce_commitment[32];
|
||||
secp256k1_musig_session_signer_data signers[2];
|
||||
secp256k1_musig_session_signer_data signers_tmp[2];
|
||||
unsigned char sk_dummy[32];
|
||||
secp256k1_xonly_pubkey pks_tmp[2];
|
||||
secp256k1_xonly_pubkey combined_pk_tmp;
|
||||
secp256k1_musig_pre_session pre_session_tmp;
|
||||
unsigned char nonce[32];
|
||||
|
||||
/* Set up signers with different public keys */
|
||||
secp256k1_testrand256(sk_dummy);
|
||||
pks_tmp[0] = pks[0];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1);
|
||||
CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0);
|
||||
/* Call get_public_nonce with different signers than the signers the session was
|
||||
* initialized with. */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, nonce, nonce_commitments, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, nonce, nonce_commitments, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1);
|
||||
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash, &session);
|
||||
}
|
||||
|
||||
/* Creates a new session (with a different session id) and tries to use that session
|
||||
* to combine nonces with given signers_other. This should fail, because the nonce
|
||||
* commitments of signers_other do not match the nonce commitments the new session
|
||||
* was initialized with. If do_test is 0, the correct signers are being used and
|
||||
* therefore the function should return 1. */
|
||||
int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) {
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_session_signer_data signers[2];
|
||||
secp256k1_musig_session_signer_data *signers_to_use;
|
||||
unsigned char nonce_commitment[32];
|
||||
unsigned char session_id[32];
|
||||
unsigned char nonce[32];
|
||||
const unsigned char *ncs[2];
|
||||
|
||||
/* Initialize new signers */
|
||||
secp256k1_testrand256(session_id);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1);
|
||||
ncs[0] = nonce_commitment_other;
|
||||
ncs[1] = nonce_commitment;
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
|
||||
secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL);
|
||||
if (do_test) {
|
||||
signers_to_use = signers_other;
|
||||
} else {
|
||||
signers_to_use = signers;
|
||||
}
|
||||
return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Initializaes a session with the given session_id, signers, pk, msg etc.
|
||||
* parameters but without a message. Will test that the message must be
|
||||
* provided with `get_public_nonce`.
|
||||
*/
|
||||
void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, unsigned char *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) {
|
||||
/* Create context for testing ARG_CHECKs by setting an illegal_callback. */
|
||||
secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
int ecount = 0;
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_musig_session_signer_data signers[2];
|
||||
unsigned char nonce_commitment[32];
|
||||
const unsigned char *ncs[2];
|
||||
unsigned char nonce[32];
|
||||
secp256k1_musig_partial_signature partial_sig;
|
||||
|
||||
secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1);
|
||||
ncs[0] = nonce_commitment_other;
|
||||
ncs[1] = nonce_commitment;
|
||||
|
||||
/* Trying to get the nonce without providing a message fails. */
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
|
||||
/* Providing a message should make get_public_nonce succeed. */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, msg) == 1);
|
||||
/* Trying to set the message again fails. */
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session, signers, nonce, ncs, 2, msg) == 0);
|
||||
CHECK(ecount == 2);
|
||||
|
||||
/* Check that it's working */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], nonce) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session, &partial_sig));
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session, &signers[1], &partial_sig, &pks[1]));
|
||||
secp256k1_context_destroy(ctx_tmp);
|
||||
}
|
||||
|
||||
void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_VERIFY);
|
||||
size_t i;
|
||||
secp256k1_musig_session session[2];
|
||||
secp256k1_musig_session_signer_data signers0[2];
|
||||
secp256k1_musig_session_signer_data signers1[2];
|
||||
unsigned char nonce_commitment[2][32];
|
||||
unsigned char session_id[2][32];
|
||||
unsigned char msg[32];
|
||||
unsigned char sk[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
unsigned char nonce[2][32];
|
||||
const unsigned char *ncs[2];
|
||||
secp256k1_musig_partial_signature partial_sig[2];
|
||||
unsigned char sig[64];
|
||||
unsigned char msghash1[32];
|
||||
unsigned char msghash2[32];
|
||||
int ecount;
|
||||
|
||||
secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount);
|
||||
ecount = 0;
|
||||
|
||||
/* Run state machine with the same objects twice to test that it's allowed to
|
||||
* reinitialize session and session_signer_data. */
|
||||
for (i = 0; i < 2; i++) {
|
||||
/* Setup */
|
||||
secp256k1_testrand256(session_id[0]);
|
||||
secp256k1_testrand256(session_id[1]);
|
||||
secp256k1_testrand256(sk[0]);
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
/* Can't combine nonces unless we're through round 1 already */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
|
||||
/* Set nonce commitments */
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1);
|
||||
/* Calling the function again is not okay */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx_tmp, &session[0], signers0, nonce[0], ncs, 2, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
|
||||
/* Get nonce for signer 1 */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1);
|
||||
|
||||
/* Set nonces */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1);
|
||||
/* Can't set nonce that doesn't match nonce commitment */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[0]) == 0);
|
||||
/* Set correct nonce */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1);
|
||||
|
||||
/* Combine nonces */
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1);
|
||||
/* Not everyone is present from signer 1's view */
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0);
|
||||
/* Make everyone present */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1);
|
||||
|
||||
/* Can't combine nonces from signers of a different session */
|
||||
CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 1) == 0);
|
||||
CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], nonce[0], msg, sk[1], signers1, 0) == 1);
|
||||
|
||||
/* Partially sign */
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1);
|
||||
/* Can't verify, sign or combine signatures until nonce is combined */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx_tmp, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx_tmp, &session[1], &partial_sig[1]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
memset(&partial_sig[1], 0, sizeof(partial_sig[1]));
|
||||
CHECK(secp256k1_musig_partial_sig_combine(ctx_tmp, &session[1], sig, partial_sig, 2) == 0);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1);
|
||||
/* messagehash should be the same as a session whose get_public_nonce was called
|
||||
* with different signers (i.e. they diff in public keys). This is because the
|
||||
* public keys of the signers is set in stone when initializing the session. */
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]);
|
||||
musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, nonce[0], sk[1], session_id[1]);
|
||||
CHECK(memcmp(msghash1, msghash2, 32) == 0);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1);
|
||||
/* Wrong signature */
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0);
|
||||
/* Can't get the public nonce until msg is set */
|
||||
musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], nonce[0], sk[1], session_id[1], msg);
|
||||
}
|
||||
secp256k1_context_destroy(ctx_tmp);
|
||||
}
|
||||
|
||||
void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
|
||||
/* Throughout this test "a" and "b" refer to two hypothetical blockchains,
|
||||
* while the indices 0 and 1 refer to the two signers. Here signer 0 is
|
||||
* sending a-coins to signer 1, while signer 1 is sending b-coins to signer
|
||||
* 0. Signer 0 produces the adaptor signatures. */
|
||||
unsigned char final_sig_a[64];
|
||||
unsigned char final_sig_b[64];
|
||||
secp256k1_musig_partial_signature partial_sig_a[2];
|
||||
secp256k1_musig_partial_signature partial_sig_b_adapted[2];
|
||||
secp256k1_musig_partial_signature partial_sig_b[2];
|
||||
unsigned char sec_adaptor[32];
|
||||
unsigned char sec_adaptor_extracted[32];
|
||||
secp256k1_pubkey pub_adaptor;
|
||||
|
||||
unsigned char seckey_a[2][32];
|
||||
unsigned char seckey_b[2][32];
|
||||
secp256k1_xonly_pubkey pk_a[2];
|
||||
secp256k1_xonly_pubkey pk_b[2];
|
||||
secp256k1_musig_pre_session pre_session_a;
|
||||
secp256k1_musig_pre_session pre_session_b;
|
||||
secp256k1_xonly_pubkey combined_pk_a;
|
||||
secp256k1_xonly_pubkey combined_pk_b;
|
||||
secp256k1_musig_session musig_session_a[2];
|
||||
secp256k1_musig_session musig_session_b[2];
|
||||
unsigned char noncommit_a[2][32];
|
||||
unsigned char noncommit_b[2][32];
|
||||
const unsigned char *noncommit_a_ptr[2];
|
||||
const unsigned char *noncommit_b_ptr[2];
|
||||
unsigned char pubnon_a[2][32];
|
||||
unsigned char pubnon_b[2][32];
|
||||
int combined_nonce_parity_a;
|
||||
int combined_nonce_parity_b;
|
||||
secp256k1_musig_session_signer_data data_a[2];
|
||||
secp256k1_musig_session_signer_data data_b[2];
|
||||
|
||||
const unsigned char seed[32] = "still tired of choosing seeds...";
|
||||
const unsigned char msg32_a[32] = "this is the message blockchain a";
|
||||
const unsigned char msg32_b[32] = "this is the message blockchain b";
|
||||
|
||||
/* Step 1: key setup */
|
||||
secp256k1_testrand256(seckey_a[0]);
|
||||
secp256k1_testrand256(seckey_a[1]);
|
||||
secp256k1_testrand256(seckey_b[0]);
|
||||
secp256k1_testrand256(seckey_b[1]);
|
||||
secp256k1_testrand256(sec_adaptor);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1]));
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor));
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2));
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2));
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1]));
|
||||
noncommit_a_ptr[0] = noncommit_a[0];
|
||||
noncommit_a_ptr[1] = noncommit_a[1];
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1]));
|
||||
noncommit_b_ptr[0] = noncommit_b[0];
|
||||
noncommit_b_ptr[1] = noncommit_b[1];
|
||||
|
||||
/* Step 2: Exchange nonces */
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, pubnon_a[0], noncommit_a_ptr, 2, NULL));
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, pubnon_a[1], noncommit_a_ptr, 2, NULL));
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, pubnon_b[0], noncommit_b_ptr, 2, NULL));
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, pubnon_b[1], noncommit_b_ptr, 2, NULL));
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], pubnon_a[0]));
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], pubnon_a[1]));
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], pubnon_b[0]));
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], pubnon_b[1]));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &combined_nonce_parity_a, &pub_adaptor));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &combined_nonce_parity_b, &pub_adaptor));
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor));
|
||||
|
||||
/* Step 3: Signer 0 produces partial signatures for both chains. */
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[0], &partial_sig_a[0]));
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[0], &partial_sig_b[0]));
|
||||
|
||||
/* Step 4: Signer 1 receives partial signatures, verifies them and creates a
|
||||
* partial signature to send B-coins to signer 0. */
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_a[1], data_a, &partial_sig_a[0], &pk_a[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_b[1], data_b, &partial_sig_b[0], &pk_b[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[1], &partial_sig_b[1]));
|
||||
|
||||
/* Step 5: Signer 0 adapts its own partial signature and combines it with the
|
||||
* partial signature from signer 1. This results in a complete signature which
|
||||
* is broadcasted by signer 0 to take B-coins. */
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, combined_nonce_parity_b));
|
||||
memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1]));
|
||||
CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1);
|
||||
|
||||
/* Step 6: Signer 1 extracts adaptor from the published signature, applies it to
|
||||
* other partial signature, and takes A-coins. */
|
||||
CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, combined_nonce_parity_b) == 1);
|
||||
CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */
|
||||
CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, combined_nonce_parity_a));
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1]));
|
||||
CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1);
|
||||
}
|
||||
|
||||
/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the
|
||||
* expected state. */
|
||||
void sha256_tag_test(void) {
|
||||
char tag[17] = "MuSig coefficient";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_tagged;
|
||||
unsigned char buf[32];
|
||||
unsigned char buf2[32];
|
||||
size_t i;
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, (unsigned char *) tag, 17);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
/* buf = SHA256("MuSig coefficient") */
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
/* Is buffer fully consumed? */
|
||||
CHECK((sha.bytes & 0x3F) == 0);
|
||||
|
||||
/* Compare with tagged SHA */
|
||||
secp256k1_musig_sha256_init_tagged(&sha_tagged);
|
||||
for (i = 0; i < 8; i++) {
|
||||
CHECK(sha_tagged.s[i] == sha.s[i]);
|
||||
}
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
secp256k1_sha256_write(&sha_tagged, buf, 32);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_sha256_finalize(&sha_tagged, buf2);
|
||||
CHECK(memcmp(buf, buf2, 32) == 0);
|
||||
}
|
||||
|
||||
/* Attempts to create a signature for the combined public key using given secret
|
||||
* keys and pre_session. */
|
||||
void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, const unsigned char *sk0, const unsigned char *sk1, secp256k1_musig_pre_session *pre_session) {
|
||||
secp256k1_musig_session session[2];
|
||||
secp256k1_musig_session_signer_data signers0[2];
|
||||
secp256k1_musig_session_signer_data signers1[2];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
unsigned char session_id[2][32];
|
||||
unsigned char msg[32];
|
||||
unsigned char nonce_commitment[2][32];
|
||||
unsigned char nonce[2][32];
|
||||
const unsigned char *ncs[2];
|
||||
secp256k1_musig_partial_signature partial_sig[2];
|
||||
unsigned char final_sig[64];
|
||||
|
||||
secp256k1_testrand256(session_id[0]);
|
||||
secp256k1_testrand256(session_id[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1);
|
||||
/* Set nonce commitments */
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, nonce[0], ncs, 2, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, nonce[1], ncs, 2, NULL) == 1);
|
||||
/* Set nonces */
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], nonce[1]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], nonce[0]) == 1);
|
||||
CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], nonce[1]) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1);
|
||||
CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2));
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, combined_pubkey) == 1);
|
||||
}
|
||||
|
||||
/* In this test we create a combined public key P and a commitment Q = P +
|
||||
* hash(P, contract)*G. Then we test that we can sign for both public keys. In
|
||||
* order to sign for Q we use the tweak32 argument of partial_sig_combine. */
|
||||
void musig_tweak_test(secp256k1_scratch_space *scratch) {
|
||||
unsigned char sk[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
secp256k1_musig_pre_session pre_session_P;
|
||||
secp256k1_musig_pre_session pre_session_Q;
|
||||
secp256k1_xonly_pubkey P;
|
||||
unsigned char P_serialized[32];
|
||||
secp256k1_pubkey Q;
|
||||
int Q_parity;
|
||||
secp256k1_xonly_pubkey Q_xonly;
|
||||
unsigned char Q_serialized[32];
|
||||
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char contract[32];
|
||||
unsigned char ec_commit_tweak[32];
|
||||
|
||||
/* Setup */
|
||||
secp256k1_testrand256(sk[0]);
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(contract);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1);
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, P_serialized, 32);
|
||||
secp256k1_sha256_write(&sha, contract, 32);
|
||||
secp256k1_sha256_finalize(&sha, ec_commit_tweak);
|
||||
pre_session_Q = pre_session_P;
|
||||
CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &pre_session_Q, &Q, &P, ec_commit_tweak) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &Q_xonly, &Q_parity, &Q));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, Q_serialized, &Q_xonly));
|
||||
/* Check that musig_pubkey_tweak_add produces same result as
|
||||
* xonly_pubkey_tweak_add. */
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, Q_serialized, Q_parity, &P, ec_commit_tweak) == 1);
|
||||
|
||||
/* Test signing for P */
|
||||
musig_tweak_test_helper(&P, sk[0], sk[1], &pre_session_P);
|
||||
/* Test signing for Q */
|
||||
musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q);
|
||||
}
|
||||
|
||||
void run_musig_tests(void) {
|
||||
int i;
|
||||
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
musig_simple_test(scratch);
|
||||
}
|
||||
musig_api_tests(scratch);
|
||||
musig_state_machine_tests(scratch);
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Run multiple times to ensure that pk and nonce have different y
|
||||
* parities */
|
||||
scriptless_atomic_swap(scratch);
|
||||
musig_tweak_test(scratch);
|
||||
}
|
||||
sha256_tag_test();
|
||||
|
||||
secp256k1_scratch_space_destroy(ctx, scratch);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -20,9 +20,9 @@
|
|||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#if defined(SECP256K1_BIG_ENDIAN)
|
||||
#define BE32(x) (x)
|
||||
#else
|
||||
#elif defined(SECP256K1_LITTLE_ENDIAN)
|
||||
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,15 @@
|
|||
#include "modules/rangeproof/rangeproof_impl.h"
|
||||
|
||||
/** Alternative generator for secp256k1.
|
||||
* This is the sha256 of 'g' after DER encoding (without compression),
|
||||
* which happens to be a point on the curve.
|
||||
* sage: G2 = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'.decode('hex')).hexdigest(),16)))
|
||||
* sage: '%x %x' % G2.xy()
|
||||
* This is the sha256 of 'g' after standard encoding (without compression),
|
||||
* which happens to be a point on the curve. More precisely, the generator is
|
||||
* derived by running the following script with the sage mathematics software.
|
||||
|
||||
import hashlib
|
||||
F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
|
||||
G = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'
|
||||
H = EllipticCurve ([F (0), F (7)]).lift_x(F(int(hashlib.sha256(G.decode('hex')).hexdigest(),16)))
|
||||
print('%x %x' % H.xy())
|
||||
*/
|
||||
static const secp256k1_generator secp256k1_generator_h_internal = {{
|
||||
0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ static void test_pedersen_api(const secp256k1_context *none, const secp256k1_con
|
|||
unsigned char blind_out[32];
|
||||
const unsigned char *blind_ptr = blind;
|
||||
unsigned char *blind_out_ptr = blind_out;
|
||||
uint64_t val = secp256k1_rand32();
|
||||
uint64_t val = secp256k1_testrand32();
|
||||
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
CHECK(secp256k1_pedersen_commit(none, &commit, blind, val, secp256k1_generator_h) == 0);
|
||||
CHECK(*ecount == 1);
|
||||
CHECK(secp256k1_pedersen_commit(vrfy, &commit, blind, val, secp256k1_generator_h) == 0);
|
||||
|
|
@ -80,8 +80,8 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
|
|||
unsigned char proof[5134];
|
||||
unsigned char blind[32];
|
||||
secp256k1_pedersen_commitment commit;
|
||||
uint64_t vmin = secp256k1_rand32();
|
||||
uint64_t val = vmin + secp256k1_rand32();
|
||||
uint64_t vmin = secp256k1_testrand32();
|
||||
uint64_t val = vmin + secp256k1_testrand32();
|
||||
size_t len = sizeof(proof);
|
||||
/* we'll switch to dylan thomas for this one */
|
||||
const unsigned char message[68] = "My tears are like the quiet drift / Of petals from some magic rose;";
|
||||
|
|
@ -89,7 +89,7 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
|
|||
const unsigned char ext_commit[72] = "And all my grief flows from the rift / Of unremembered skies and snows.";
|
||||
size_t ext_commit_len = sizeof(ext_commit);
|
||||
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, val, secp256k1_generator_h));
|
||||
|
||||
CHECK(secp256k1_rangeproof_sign(none, proof, &len, vmin, &commit, blind, commit.data, 0, 0, val, message, mlen, ext_commit, ext_commit_len, secp256k1_generator_h) == 0);
|
||||
|
|
@ -271,8 +271,8 @@ static void test_pedersen(void) {
|
|||
int inputs;
|
||||
int outputs;
|
||||
int total;
|
||||
inputs = (secp256k1_rand32() & 7) + 1;
|
||||
outputs = (secp256k1_rand32() & 7) + 2;
|
||||
inputs = (secp256k1_testrand32() & 7) + 1;
|
||||
outputs = (secp256k1_testrand32() & 7) + 2;
|
||||
total = inputs + outputs;
|
||||
for (i = 0; i < 19; i++) {
|
||||
cptr[i] = &commits[i];
|
||||
|
|
@ -280,11 +280,11 @@ static void test_pedersen(void) {
|
|||
}
|
||||
totalv = 0;
|
||||
for (i = 0; i < inputs; i++) {
|
||||
values[i] = secp256k1_rands64(0, INT64_MAX - totalv);
|
||||
values[i] = secp256k1_testrandi64(0, INT64_MAX - totalv);
|
||||
totalv += values[i];
|
||||
}
|
||||
for (i = 0; i < outputs - 1; i++) {
|
||||
values[i + inputs] = secp256k1_rands64(0, totalv);
|
||||
values[i + inputs] = secp256k1_testrandi64(0, totalv);
|
||||
totalv -= values[i + inputs];
|
||||
}
|
||||
values[total - 1] = totalv;
|
||||
|
|
@ -331,27 +331,27 @@ static void test_borromean(void) {
|
|||
size_t i;
|
||||
size_t j;
|
||||
int c;
|
||||
secp256k1_rand256_test(m);
|
||||
nrings = 1 + (secp256k1_rand32()&7);
|
||||
secp256k1_testrand256_test(m);
|
||||
nrings = 1 + (secp256k1_testrand32()&7);
|
||||
c = 0;
|
||||
secp256k1_scalar_set_int(&one, 1);
|
||||
if (secp256k1_rand32()&1) {
|
||||
if (secp256k1_testrand32()&1) {
|
||||
secp256k1_scalar_negate(&one, &one);
|
||||
}
|
||||
for (i = 0; i < nrings; i++) {
|
||||
rsizes[i] = 1 + (secp256k1_rand32()&7);
|
||||
secidx[i] = secp256k1_rand32() % rsizes[i];
|
||||
rsizes[i] = 1 + (secp256k1_testrand32()&7);
|
||||
secidx[i] = secp256k1_testrand32() % rsizes[i];
|
||||
random_scalar_order(&sec[i]);
|
||||
random_scalar_order(&k[i]);
|
||||
if(secp256k1_rand32()&7) {
|
||||
if(secp256k1_testrand32()&7) {
|
||||
sec[i] = one;
|
||||
}
|
||||
if(secp256k1_rand32()&7) {
|
||||
if(secp256k1_testrand32()&7) {
|
||||
k[i] = one;
|
||||
}
|
||||
for (j = 0; j < rsizes[i]; j++) {
|
||||
random_scalar_order(&s[c + j]);
|
||||
if(secp256k1_rand32()&7) {
|
||||
if(secp256k1_testrand32()&7) {
|
||||
s[i] = one;
|
||||
}
|
||||
if (j == secidx[i]) {
|
||||
|
|
@ -365,14 +365,14 @@ static void test_borromean(void) {
|
|||
}
|
||||
CHECK(secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, e0, s, pubs, k, sec, rsizes, secidx, nrings, m, 32));
|
||||
CHECK(secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32));
|
||||
i = secp256k1_rand32() % c;
|
||||
i = secp256k1_testrand32() % c;
|
||||
secp256k1_scalar_negate(&s[i],&s[i]);
|
||||
CHECK(!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, e0, s, pubs, rsizes, nrings, m, 32));
|
||||
secp256k1_scalar_negate(&s[i],&s[i]);
|
||||
secp256k1_scalar_set_int(&one, 1);
|
||||
for(j = 0; j < 4; j++) {
|
||||
i = secp256k1_rand32() % c;
|
||||
if (secp256k1_rand32() & 1) {
|
||||
i = secp256k1_testrand32() % c;
|
||||
if (secp256k1_testrand32() & 1) {
|
||||
secp256k1_gej_double_var(&pubs[i],&pubs[i], NULL);
|
||||
} else {
|
||||
secp256k1_scalar_add(&s[i],&s[i],&one);
|
||||
|
|
@ -408,7 +408,7 @@ static void test_rangeproof(void) {
|
|||
memcpy(&message_long[i], message_short, sizeof(message_short));
|
||||
}
|
||||
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
for (i = 0; i < 11; i++) {
|
||||
v = testvs[i];
|
||||
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
|
||||
|
|
@ -464,7 +464,7 @@ static void test_rangeproof(void) {
|
|||
CHECK(maxv == v);
|
||||
}
|
||||
}
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
v = INT64_MAX - 1;
|
||||
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
|
||||
for (i = 0; i < 19; i++) {
|
||||
|
|
@ -477,10 +477,10 @@ static void test_rangeproof(void) {
|
|||
/* Make sure it fails when validating with a committed message */
|
||||
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h));
|
||||
}
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
{
|
||||
/*Malleability test.*/
|
||||
v = secp256k1_rands64(0, 255);
|
||||
v = secp256k1_testrandi64(0, 255);
|
||||
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
|
||||
len = 5134;
|
||||
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h));
|
||||
|
|
@ -498,22 +498,22 @@ static void test_rangeproof(void) {
|
|||
CHECK(maxv >= v);
|
||||
}
|
||||
memcpy(&commit2, &commit, sizeof(commit));
|
||||
for (i = 0; i < (size_t) 2*count; i++) {
|
||||
for (i = 0; i < (size_t) count; i++) {
|
||||
int exp;
|
||||
int min_bits;
|
||||
v = secp256k1_rands64(0, UINT64_MAX >> (secp256k1_rand32()&63));
|
||||
v = secp256k1_testrandi64(0, UINT64_MAX >> (secp256k1_testrand32()&63));
|
||||
vmin = 0;
|
||||
if ((v < INT64_MAX) && (secp256k1_rand32()&1)) {
|
||||
vmin = secp256k1_rands64(0, v);
|
||||
if ((v < INT64_MAX) && (secp256k1_testrand32()&1)) {
|
||||
vmin = secp256k1_testrandi64(0, v);
|
||||
}
|
||||
secp256k1_rand256(blind);
|
||||
secp256k1_testrand256(blind);
|
||||
CHECK(secp256k1_pedersen_commit(ctx, &commit, blind, v, secp256k1_generator_h));
|
||||
len = 5134;
|
||||
exp = (int)secp256k1_rands64(0,18)-(int)secp256k1_rands64(0,18);
|
||||
exp = (int)secp256k1_testrandi64(0,18)-(int)secp256k1_testrandi64(0,18);
|
||||
if (exp < 0) {
|
||||
exp = -exp;
|
||||
}
|
||||
min_bits = (int)secp256k1_rands64(0,64)-(int)secp256k1_rands64(0,64);
|
||||
min_bits = (int)secp256k1_testrandi64(0,64)-(int)secp256k1_testrandi64(0,64);
|
||||
if (min_bits < 0) {
|
||||
min_bits = -min_bits;
|
||||
}
|
||||
|
|
@ -532,23 +532,23 @@ static void test_rangeproof(void) {
|
|||
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
|
||||
memcpy(&commit2, &commit, sizeof(commit));
|
||||
}
|
||||
for (j = 0; j < 5; j++) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
for (i = 0; i < 96; i++) {
|
||||
secp256k1_rand256(&proof[i * 32]);
|
||||
secp256k1_testrand256(&proof[i * 32]);
|
||||
}
|
||||
for (k = 0; k < 128; k++) {
|
||||
for (k = 0; k < 128; k += 3) {
|
||||
len = k;
|
||||
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h));
|
||||
}
|
||||
len = secp256k1_rands64(0, 3072);
|
||||
len = secp256k1_testrandi64(0, 3072);
|
||||
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit2, proof, len, NULL, 0, secp256k1_generator_h));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_N_GENS 30
|
||||
void test_multiple_generators(void) {
|
||||
const size_t n_inputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1;
|
||||
const size_t n_outputs = (secp256k1_rand32() % (MAX_N_GENS / 2)) + 1;
|
||||
const size_t n_inputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1;
|
||||
const size_t n_outputs = (secp256k1_testrand32() % (MAX_N_GENS / 2)) + 1;
|
||||
const size_t n_generators = n_inputs + n_outputs;
|
||||
unsigned char *generator_blind[MAX_N_GENS];
|
||||
unsigned char *pedersen_blind[MAX_N_GENS];
|
||||
|
|
@ -582,11 +582,11 @@ void test_multiple_generators(void) {
|
|||
/* Compute all the values -- can be positive or negative */
|
||||
total_value = 0;
|
||||
for (i = 0; i < n_outputs; i++) {
|
||||
value[n_inputs + i] = secp256k1_rands64(0, INT64_MAX - total_value);
|
||||
value[n_inputs + i] = secp256k1_testrandi64(0, INT64_MAX - total_value);
|
||||
total_value += value[n_inputs + i];
|
||||
}
|
||||
for (i = 0; i < n_inputs - 1; i++) {
|
||||
value[i] = secp256k1_rands64(0, total_value);
|
||||
value[i] = secp256k1_testrandi64(0, total_value);
|
||||
total_value -= value[i];
|
||||
}
|
||||
value[i] = total_value;
|
||||
|
|
@ -696,10 +696,10 @@ void run_rangeproof_tests(void) {
|
|||
test_api();
|
||||
test_rangeproof_fixed_vectors();
|
||||
test_pedersen_commitment_fixed_vector();
|
||||
for (i = 0; i < 10*count; i++) {
|
||||
for (i = 0; i < count / 2 + 1; i++) {
|
||||
test_pedersen();
|
||||
}
|
||||
for (i = 0; i < 10*count; i++) {
|
||||
for (i = 0; i < count / 2 + 1; i++) {
|
||||
test_borromean();
|
||||
}
|
||||
test_rangeproof();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
include_HEADERS += include/secp256k1_recovery.h
|
||||
noinst_HEADERS += src/modules/recovery/main_impl.h
|
||||
noinst_HEADERS += src/modules/recovery/tests_impl.h
|
||||
noinst_HEADERS += src/modules/recovery/tests_exhaustive_impl.h
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_recover
|
||||
bench_recover_SOURCES = src/bench_recover.c
|
||||
|
|
|
|||
39
src/modules/recovery/main_impl.h
Executable file → Normal file
39
src/modules/recovery/main_impl.h
Executable file → Normal file
|
|
@ -122,48 +122,15 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons
|
|||
|
||||
int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
secp256k1_scalar r, s;
|
||||
secp256k1_scalar sec, non, msg;
|
||||
int recid;
|
||||
int ret = 0;
|
||||
int overflow = 0;
|
||||
int ret, recid;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(signature != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
|
||||
/* Fail if the secret key is invalid. */
|
||||
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
|
||||
unsigned char nonce32[32];
|
||||
unsigned int count = 0;
|
||||
secp256k1_scalar_set_b32(&msg, msg32, NULL);
|
||||
while (1) {
|
||||
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
|
||||
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
|
||||
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_scalar_clear(&msg);
|
||||
secp256k1_scalar_clear(&non);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
}
|
||||
if (ret) {
|
||||
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
|
||||
} else {
|
||||
memset(signature, 0, sizeof(*signature));
|
||||
}
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
|
||||
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
149
src/modules/recovery/tests_exhaustive_impl.h
Normal file
149
src/modules/recovery/tests_exhaustive_impl.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2016 Andrew Poelstra *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
|
||||
#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
|
||||
|
||||
#include "src/modules/recovery/main_impl.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
|
||||
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
|
||||
int i, j, k;
|
||||
uint64_t iter = 0;
|
||||
|
||||
/* Loop */
|
||||
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */
|
||||
for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */
|
||||
if (skip_section(&iter)) continue;
|
||||
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
|
||||
const int starting_k = k;
|
||||
secp256k1_fe r_dot_y_normalized;
|
||||
secp256k1_ecdsa_recoverable_signature rsig;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_scalar sk, msg, r, s, expected_r;
|
||||
unsigned char sk32[32], msg32[32];
|
||||
int expected_recid;
|
||||
int recid;
|
||||
int overflow;
|
||||
secp256k1_scalar_set_int(&msg, i);
|
||||
secp256k1_scalar_set_int(&sk, j);
|
||||
secp256k1_scalar_get_b32(sk32, &sk);
|
||||
secp256k1_scalar_get_b32(msg32, &msg);
|
||||
|
||||
secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
|
||||
|
||||
/* Check directly */
|
||||
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
|
||||
r_from_k(&expected_r, group, k, &overflow);
|
||||
CHECK(r == expected_r);
|
||||
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
|
||||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
|
||||
/* The recid's second bit is for conveying overflow (R.x value >= group order).
|
||||
* In the actual secp256k1 this is an astronomically unlikely event, but in the
|
||||
* small group used here, it will be the case for all points except the ones where
|
||||
* R.x=1 (which the group is specifically selected to have).
|
||||
* Note that this isn't actually useful; full recovery would need to convey
|
||||
* floor(R.x / group_order), but only one bit is used as that is sufficient
|
||||
* in the real group. */
|
||||
expected_recid = overflow ? 2 : 0;
|
||||
r_dot_y_normalized = group[k].y;
|
||||
secp256k1_fe_normalize(&r_dot_y_normalized);
|
||||
/* Also the recovery id is flipped depending if we hit the low-s branch */
|
||||
if ((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER) {
|
||||
expected_recid |= secp256k1_fe_is_odd(&r_dot_y_normalized);
|
||||
} else {
|
||||
expected_recid |= !secp256k1_fe_is_odd(&r_dot_y_normalized);
|
||||
}
|
||||
CHECK(recid == expected_recid);
|
||||
|
||||
/* Convert to a standard sig then check */
|
||||
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
|
||||
/* Note that we compute expected_r *after* signing -- this is important
|
||||
* because our nonce-computing function function might change k during
|
||||
* signing. */
|
||||
r_from_k(&expected_r, group, k, NULL);
|
||||
CHECK(r == expected_r);
|
||||
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
|
||||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
|
||||
|
||||
/* Overflow means we've tried every possible nonce */
|
||||
if (k < starting_k) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
|
||||
/* This is essentially a copy of test_exhaustive_verify, with recovery added */
|
||||
int s, r, msg, key;
|
||||
uint64_t iter = 0;
|
||||
for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) {
|
||||
for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) {
|
||||
for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) {
|
||||
for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) {
|
||||
secp256k1_ge nonconst_ge;
|
||||
secp256k1_ecdsa_recoverable_signature rsig;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pk;
|
||||
secp256k1_scalar sk_s, msg_s, r_s, s_s;
|
||||
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
|
||||
int recid = 0;
|
||||
int k, should_verify;
|
||||
unsigned char msg32[32];
|
||||
|
||||
if (skip_section(&iter)) continue;
|
||||
|
||||
secp256k1_scalar_set_int(&s_s, s);
|
||||
secp256k1_scalar_set_int(&r_s, r);
|
||||
secp256k1_scalar_set_int(&msg_s, msg);
|
||||
secp256k1_scalar_set_int(&sk_s, key);
|
||||
secp256k1_scalar_get_b32(msg32, &msg_s);
|
||||
|
||||
/* Verify by hand */
|
||||
/* Run through every k value that gives us this r and check that *one* works.
|
||||
* Note there could be none, there could be multiple, ECDSA is weird. */
|
||||
should_verify = 0;
|
||||
for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
|
||||
secp256k1_scalar check_x_s;
|
||||
r_from_k(&check_x_s, group, k, NULL);
|
||||
if (r_s == check_x_s) {
|
||||
secp256k1_scalar_set_int(&s_times_k_s, k);
|
||||
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
|
||||
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
|
||||
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
|
||||
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
|
||||
}
|
||||
}
|
||||
/* nb we have a "high s" rule */
|
||||
should_verify &= !secp256k1_scalar_is_high(&s_s);
|
||||
|
||||
/* We would like to try recovering the pubkey and checking that it matches,
|
||||
* but pubkey recovery is impossible in the exhaustive tests (the reason
|
||||
* being that there are 12 nonzero r values, 12 nonzero points, and no
|
||||
* overlap between the sets, so there are no valid signatures). */
|
||||
|
||||
/* Verify by converting to a standard signature and calling verify */
|
||||
secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
|
||||
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
|
||||
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
|
||||
secp256k1_pubkey_save(&pk, &nonconst_ge);
|
||||
CHECK(should_verify ==
|
||||
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_exhaustive_recovery(const secp256k1_context *ctx, const secp256k1_ge *group) {
|
||||
test_exhaustive_recovery_sign(ctx, group);
|
||||
test_exhaustive_recovery_verify(ctx, group);
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H */
|
||||
|
|
@ -25,7 +25,7 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c
|
|||
}
|
||||
/* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */
|
||||
memset(nonce32, 1, 32);
|
||||
return secp256k1_rand_bits(1);
|
||||
return secp256k1_testrand_bits(1);
|
||||
}
|
||||
|
||||
void test_ecdsa_recovery_api(void) {
|
||||
|
|
@ -184,7 +184,7 @@ void test_ecdsa_recovery_end_to_end(void) {
|
|||
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
|
||||
CHECK(memcmp(&signature[4], &signature[0], 64) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&signature[4], &signature[0], 64) == 0);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1);
|
||||
memset(&rsignature[4], 0, sizeof(rsignature[4]));
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
|
||||
|
|
@ -193,16 +193,16 @@ void test_ecdsa_recovery_end_to_end(void) {
|
|||
/* Parse compact (with recovery id) and recover. */
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
|
||||
CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1);
|
||||
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
|
||||
CHECK(secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
|
||||
/* Serialize/destroy/parse signature and verify again. */
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
|
||||
sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
|
||||
sig[secp256k1_testrand_bits(6)] += 1 + secp256k1_testrand_int(255);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0);
|
||||
/* Recover again */
|
||||
CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 ||
|
||||
memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
|
||||
secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
|
||||
}
|
||||
|
||||
/* Tests several edge cases. */
|
||||
|
|
@ -215,7 +215,7 @@ void test_ecdsa_recovery_edge_cases(void) {
|
|||
};
|
||||
const unsigned char sig64[64] = {
|
||||
/* Generated by signing the above message with nonce 'This is the nonce we will use...'
|
||||
* and secret key 0 (which is not valid), resulting in recid 0. */
|
||||
* and secret key 0 (which is not valid), resulting in recid 1. */
|
||||
0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8,
|
||||
0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96,
|
||||
0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63,
|
||||
|
|
|
|||
9
src/modules/schnorrsig/Makefile.am.include
Normal file
9
src/modules/schnorrsig/Makefile.am.include
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
include_HEADERS += include/secp256k1_schnorrsig.h
|
||||
noinst_HEADERS += src/modules/schnorrsig/main_impl.h
|
||||
noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
|
||||
noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_schnorrsig
|
||||
bench_schnorrsig_SOURCES = src/bench_schnorrsig.c
|
||||
bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
|
||||
endif
|
||||
239
src/modules/schnorrsig/main_impl.h
Normal file
239
src/modules/schnorrsig/main_impl.h
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_
|
||||
#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_schnorrsig.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */
|
||||
static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x46615b35ul;
|
||||
sha->s[1] = 0xf4bfbff7ul;
|
||||
sha->s[2] = 0x9f8dc671ul;
|
||||
sha->s[3] = 0x83627ab3ul;
|
||||
sha->s[4] = 0x60217180ul;
|
||||
sha->s[5] = 0x57358661ul;
|
||||
sha->s[6] = 0x21a29e54ul;
|
||||
sha->s[7] = 0x68b07b4cul;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */
|
||||
static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x24dd3219ul;
|
||||
sha->s[1] = 0x4eba7e70ul;
|
||||
sha->s[2] = 0xca0fabb9ul;
|
||||
sha->s[3] = 0x0fa3166dul;
|
||||
sha->s[4] = 0x3afbe4b1ul;
|
||||
sha->s[5] = 0x4c44df97ul;
|
||||
sha->s[6] = 0x4aac2739ul;
|
||||
sha->s[7] = 0x249e850aul;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
|
||||
* by using the correct tagged hash function. */
|
||||
static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0";
|
||||
|
||||
static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char masked_key[32];
|
||||
int i;
|
||||
|
||||
if (algo16 == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha);
|
||||
secp256k1_sha256_write(&sha, data, 32);
|
||||
secp256k1_sha256_finalize(&sha, masked_key);
|
||||
for (i = 0; i < 32; i++) {
|
||||
masked_key[i] ^= key32[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tag the hash with algo16 which is important to avoid nonce reuse across
|
||||
* algorithms. If this nonce function is used in BIP-340 signing as defined
|
||||
* in the spec, an optimized tagging implementation is used. */
|
||||
if (secp256k1_memcmp_var(algo16, bip340_algo16, 16) == 0) {
|
||||
secp256k1_nonce_function_bip340_sha256_tagged(&sha);
|
||||
} else {
|
||||
int algo16_len = 16;
|
||||
/* Remove terminating null bytes */
|
||||
while (algo16_len > 0 && !algo16[algo16_len - 1]) {
|
||||
algo16_len--;
|
||||
}
|
||||
secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len);
|
||||
}
|
||||
|
||||
/* Hash (masked-)key||pk||msg using the tagged hash as per the spec */
|
||||
if (data != NULL) {
|
||||
secp256k1_sha256_write(&sha, masked_key, 32);
|
||||
} else {
|
||||
secp256k1_sha256_write(&sha, key32, 32);
|
||||
}
|
||||
secp256k1_sha256_write(&sha, xonly_pk32, 32);
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, nonce32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340;
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */
|
||||
static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x9cecba11ul;
|
||||
sha->s[1] = 0x23925381ul;
|
||||
sha->s[2] = 0x11679112ul;
|
||||
sha->s[3] = 0xd1627e0ful;
|
||||
sha->s[4] = 0x97c87550ul;
|
||||
sha->s[5] = 0x003cc765ul;
|
||||
sha->s[6] = 0x90f61164ul;
|
||||
sha->s[7] = 0x33e9b66aul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg32, const unsigned char *pubkey32)
|
||||
{
|
||||
unsigned char buf[32];
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
/* tagged hash(r.x, pk.x, msg32) */
|
||||
secp256k1_schnorrsig_sha256_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, r32, 32);
|
||||
secp256k1_sha256_write(&sha, pubkey32, 32);
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
/* Set scalar e to the challenge hash modulo the curve order as per
|
||||
* BIP340. */
|
||||
secp256k1_scalar_set_b32(e, buf, NULL);
|
||||
}
|
||||
|
||||
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
|
||||
secp256k1_scalar sk;
|
||||
secp256k1_scalar e;
|
||||
secp256k1_scalar k;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge pk;
|
||||
secp256k1_ge r;
|
||||
unsigned char buf[32] = { 0 };
|
||||
unsigned char pk_buf[32];
|
||||
unsigned char seckey[32];
|
||||
int ret = 1;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(keypair != NULL);
|
||||
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_bip340;
|
||||
}
|
||||
|
||||
ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
|
||||
/* Because we are signing for a x-only pubkey, the secret key is negated
|
||||
* before signing if the point corresponding to the secret key does not
|
||||
* have an even Y. */
|
||||
if (secp256k1_fe_is_odd(&pk.y)) {
|
||||
secp256k1_scalar_negate(&sk, &sk);
|
||||
}
|
||||
|
||||
secp256k1_scalar_get_b32(seckey, &sk);
|
||||
secp256k1_fe_get_b32(pk_buf, &pk.x);
|
||||
ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata);
|
||||
secp256k1_scalar_set_b32(&k, buf, NULL);
|
||||
ret &= !secp256k1_scalar_is_zero(&k);
|
||||
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
|
||||
secp256k1_ge_set_gej(&r, &rj);
|
||||
|
||||
/* We declassify r to allow using it as a branch point. This is fine
|
||||
* because r is not a secret. */
|
||||
secp256k1_declassify(ctx, &r, sizeof(r));
|
||||
secp256k1_fe_normalize_var(&r.y);
|
||||
if (secp256k1_fe_is_odd(&r.y)) {
|
||||
secp256k1_scalar_negate(&k, &k);
|
||||
}
|
||||
secp256k1_fe_normalize_var(&r.x);
|
||||
secp256k1_fe_get_b32(&sig64[0], &r.x);
|
||||
|
||||
secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, pk_buf);
|
||||
secp256k1_scalar_mul(&e, &e, &sk);
|
||||
secp256k1_scalar_add(&e, &e, &k);
|
||||
secp256k1_scalar_get_b32(&sig64[32], &e);
|
||||
|
||||
secp256k1_memczero(sig64, 64, !ret);
|
||||
secp256k1_scalar_clear(&k);
|
||||
secp256k1_scalar_clear(&sk);
|
||||
memset(seckey, 0, sizeof(seckey));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
|
||||
secp256k1_scalar s;
|
||||
secp256k1_scalar e;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge pk;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_fe rx;
|
||||
secp256k1_ge r;
|
||||
unsigned char buf[32];
|
||||
int overflow;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&s, &sig64[32], &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compute e. */
|
||||
secp256k1_fe_get_b32(buf, &pk.x);
|
||||
secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, buf);
|
||||
|
||||
/* Compute rj = s*G + (-e)*pkj */
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
secp256k1_gej_set_ge(&pkj, &pk);
|
||||
secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
|
||||
|
||||
secp256k1_ge_set_gej_var(&r, &rj);
|
||||
if (secp256k1_ge_is_infinity(&r)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_fe_normalize_var(&r.y);
|
||||
return !secp256k1_fe_is_odd(&r.y) &&
|
||||
secp256k1_fe_equal_var(&rx, &r.x);
|
||||
}
|
||||
|
||||
#endif
|
||||
206
src/modules/schnorrsig/tests_exhaustive_impl.h
Normal file
206
src/modules/schnorrsig/tests_exhaustive_impl.h
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2020 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
|
||||
#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_
|
||||
|
||||
#include "include/secp256k1_schnorrsig.h"
|
||||
#include "src/modules/schnorrsig/main_impl.h"
|
||||
|
||||
static const unsigned char invalid_pubkey_bytes[][32] = {
|
||||
/* 0 */
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
/* 2 */
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
|
||||
},
|
||||
/* order */
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 24) & 0xFF,
|
||||
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 16) & 0xFF,
|
||||
((EXHAUSTIVE_TEST_ORDER + 0UL) >> 8) & 0xFF,
|
||||
(EXHAUSTIVE_TEST_ORDER + 0UL) & 0xFF
|
||||
},
|
||||
/* order + 1 */
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 24) & 0xFF,
|
||||
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 16) & 0xFF,
|
||||
((EXHAUSTIVE_TEST_ORDER + 1UL) >> 8) & 0xFF,
|
||||
(EXHAUSTIVE_TEST_ORDER + 1UL) & 0xFF
|
||||
},
|
||||
/* field size */
|
||||
{
|
||||
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
|
||||
},
|
||||
/* field size + 1 (note that 1 is legal) */
|
||||
{
|
||||
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, 0x30
|
||||
},
|
||||
/* 2^256 - 1 */
|
||||
{
|
||||
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, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
}
|
||||
};
|
||||
|
||||
#define NUM_INVALID_KEYS (sizeof(invalid_pubkey_bytes) / sizeof(invalid_pubkey_bytes[0]))
|
||||
|
||||
static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
|
||||
const unsigned char *key32, const unsigned char *xonly_pk32,
|
||||
const unsigned char *algo16, void* data) {
|
||||
secp256k1_scalar s;
|
||||
int *idata = data;
|
||||
(void)msg32;
|
||||
(void)key32;
|
||||
(void)xonly_pk32;
|
||||
(void)algo16;
|
||||
secp256k1_scalar_set_int(&s, *idata);
|
||||
secp256k1_scalar_get_b32(nonce32, &s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey* pubkeys, unsigned char (*xonly_pubkey_bytes)[32], const int* parities) {
|
||||
int d;
|
||||
uint64_t iter = 0;
|
||||
/* Iterate over the possible public keys to verify against (through their corresponding DL d). */
|
||||
for (d = 1; d <= EXHAUSTIVE_TEST_ORDER / 2; ++d) {
|
||||
int actual_d;
|
||||
unsigned k;
|
||||
unsigned char pk32[32];
|
||||
memcpy(pk32, xonly_pubkey_bytes[d - 1], 32);
|
||||
actual_d = parities[d - 1] ? EXHAUSTIVE_TEST_ORDER - d : d;
|
||||
/* Iterate over the possible valid first 32 bytes in the signature, through their corresponding DL k.
|
||||
Values above EXHAUSTIVE_TEST_ORDER/2 refer to the entries in invalid_pubkey_bytes. */
|
||||
for (k = 1; k <= EXHAUSTIVE_TEST_ORDER / 2 + NUM_INVALID_KEYS; ++k) {
|
||||
unsigned char sig64[64];
|
||||
int actual_k = -1;
|
||||
int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
|
||||
int e_count_done = 0;
|
||||
if (skip_section(&iter)) continue;
|
||||
if (k <= EXHAUSTIVE_TEST_ORDER / 2) {
|
||||
memcpy(sig64, xonly_pubkey_bytes[k - 1], 32);
|
||||
actual_k = parities[k - 1] ? EXHAUSTIVE_TEST_ORDER - k : k;
|
||||
} else {
|
||||
memcpy(sig64, invalid_pubkey_bytes[k - 1 - EXHAUSTIVE_TEST_ORDER / 2], 32);
|
||||
}
|
||||
/* Randomly generate messages until all challenges have been hit. */
|
||||
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
|
||||
secp256k1_scalar e;
|
||||
unsigned char msg32[32];
|
||||
secp256k1_testrand256(msg32);
|
||||
secp256k1_schnorrsig_challenge(&e, sig64, msg32, pk32);
|
||||
/* Only do work if we hit a challenge we haven't tried before. */
|
||||
if (!e_done[e]) {
|
||||
/* Iterate over the possible valid last 32 bytes in the signature.
|
||||
0..order=that s value; order+1=random bytes */
|
||||
int count_valid = 0, s;
|
||||
for (s = 0; s <= EXHAUSTIVE_TEST_ORDER + 1; ++s) {
|
||||
int expect_valid, valid;
|
||||
if (s <= EXHAUSTIVE_TEST_ORDER) {
|
||||
secp256k1_scalar s_s;
|
||||
secp256k1_scalar_set_int(&s_s, s);
|
||||
secp256k1_scalar_get_b32(sig64 + 32, &s_s);
|
||||
expect_valid = actual_k != -1 && s != EXHAUSTIVE_TEST_ORDER &&
|
||||
(s_s == (actual_k + actual_d * e) % EXHAUSTIVE_TEST_ORDER);
|
||||
} else {
|
||||
secp256k1_testrand256(sig64 + 32);
|
||||
expect_valid = 0;
|
||||
}
|
||||
valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, &pubkeys[d - 1]);
|
||||
CHECK(valid == expect_valid);
|
||||
count_valid += valid;
|
||||
}
|
||||
/* Exactly one s value must verify, unless R is illegal. */
|
||||
CHECK(count_valid == (actual_k != -1));
|
||||
/* Don't retry other messages that result in the same challenge. */
|
||||
e_done[e] = 1;
|
||||
++e_count_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
|
||||
int d, k;
|
||||
uint64_t iter = 0;
|
||||
/* Loop over keys. */
|
||||
for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
|
||||
int actual_d = d;
|
||||
if (parities[d - 1]) actual_d = EXHAUSTIVE_TEST_ORDER - d;
|
||||
/* Loop over nonces. */
|
||||
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; ++k) {
|
||||
int e_done[EXHAUSTIVE_TEST_ORDER] = {0};
|
||||
int e_count_done = 0;
|
||||
unsigned char msg32[32];
|
||||
unsigned char sig64[64];
|
||||
int actual_k = k;
|
||||
if (skip_section(&iter)) continue;
|
||||
if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
|
||||
/* Generate random messages until all challenges have been tried. */
|
||||
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
|
||||
secp256k1_scalar e;
|
||||
secp256k1_testrand256(msg32);
|
||||
secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, xonly_pubkey_bytes[d - 1]);
|
||||
/* Only do work if we hit a challenge we haven't tried before. */
|
||||
if (!e_done[e]) {
|
||||
secp256k1_scalar expected_s = (actual_k + e * actual_d) % EXHAUSTIVE_TEST_ORDER;
|
||||
unsigned char expected_s_bytes[32];
|
||||
secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
|
||||
/* Invoke the real function to construct a signature. */
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig64, msg32, &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
|
||||
/* The first 32 bytes must match the xonly pubkey for the specified k. */
|
||||
CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
|
||||
/* The last 32 bytes must match the expected s value. */
|
||||
CHECK(secp256k1_memcmp_var(sig64 + 32, expected_s_bytes, 32) == 0);
|
||||
/* Don't retry other messages that result in the same challenge. */
|
||||
e_done[e] = 1;
|
||||
++e_count_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_exhaustive_schnorrsig(const secp256k1_context *ctx) {
|
||||
secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
int parity[EXHAUSTIVE_TEST_ORDER - 1];
|
||||
unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32];
|
||||
unsigned i;
|
||||
|
||||
/* Verify that all invalid_pubkey_bytes are actually invalid. */
|
||||
for (i = 0; i < NUM_INVALID_KEYS; ++i) {
|
||||
secp256k1_xonly_pubkey pk;
|
||||
CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk, invalid_pubkey_bytes[i]));
|
||||
}
|
||||
|
||||
/* Construct keypairs and xonly-pubkeys for the entire group. */
|
||||
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; ++i) {
|
||||
secp256k1_scalar scalar_i;
|
||||
unsigned char buf[32];
|
||||
secp256k1_scalar_set_int(&scalar_i, i);
|
||||
secp256k1_scalar_get_b32(buf, &scalar_i);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf));
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parity[i - 1], &keypair[i - 1]));
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1]));
|
||||
}
|
||||
|
||||
test_exhaustive_schnorrsig_sign(ctx, xonly_pubkey_bytes, keypair, parity);
|
||||
test_exhaustive_schnorrsig_verify(ctx, xonly_pubkey, xonly_pubkey_bytes, parity);
|
||||
}
|
||||
|
||||
#endif
|
||||
806
src/modules/schnorrsig/tests_impl.h
Normal file
806
src/modules/schnorrsig/tests_impl.h
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_
|
||||
#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_
|
||||
|
||||
#include "secp256k1_schnorrsig.h"
|
||||
|
||||
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
|
||||
* bytes) changes the hash function
|
||||
*/
|
||||
void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
|
||||
unsigned char nonces[2][32];
|
||||
CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1);
|
||||
secp256k1_testrand_flip(args[n_flip], n_bytes);
|
||||
CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1);
|
||||
CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0);
|
||||
}
|
||||
|
||||
/* Tests for the equality of two sha256 structs. This function only produces a
|
||||
* correct result if an integer multiple of 64 many bytes have been written
|
||||
* into the hash functions. */
|
||||
void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) {
|
||||
/* Is buffer fully consumed? */
|
||||
CHECK((sha1->bytes & 0x3F) == 0);
|
||||
|
||||
CHECK(sha1->bytes == sha2->bytes);
|
||||
CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0);
|
||||
}
|
||||
|
||||
void run_nonce_function_bip340_tests(void) {
|
||||
unsigned char tag[13] = "BIP0340/nonce";
|
||||
unsigned char aux_tag[11] = "BIP0340/aux";
|
||||
unsigned char algo16[16] = "BIP0340/nonce\0\0\0";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_optimized;
|
||||
unsigned char nonce[32];
|
||||
unsigned char msg[32];
|
||||
unsigned char key[32];
|
||||
unsigned char pk[32];
|
||||
unsigned char aux_rand[32];
|
||||
unsigned char *args[5];
|
||||
int i;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
secp256k1_testrand256(msg);
|
||||
secp256k1_testrand256(key);
|
||||
secp256k1_testrand256(pk);
|
||||
secp256k1_testrand256(aux_rand);
|
||||
|
||||
/* Check that a bitflip in an argument results in different nonces. */
|
||||
args[0] = msg;
|
||||
args[1] = key;
|
||||
args[2] = pk;
|
||||
args[3] = algo16;
|
||||
args[4] = aux_rand;
|
||||
for (i = 0; i < count; i++) {
|
||||
nonce_function_bip340_bitflip(args, 0, 32);
|
||||
nonce_function_bip340_bitflip(args, 1, 32);
|
||||
nonce_function_bip340_bitflip(args, 2, 32);
|
||||
/* Flip algo16 special case "BIP0340/nonce" */
|
||||
nonce_function_bip340_bitflip(args, 3, 16);
|
||||
/* Flip algo16 again */
|
||||
nonce_function_bip340_bitflip(args, 3, 16);
|
||||
nonce_function_bip340_bitflip(args, 4, 32);
|
||||
}
|
||||
|
||||
/* NULL algo16 is disallowed */
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0);
|
||||
/* Empty algo16 is fine */
|
||||
memset(algo16, 0x00, 16);
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
/* algo16 with terminating null bytes is fine */
|
||||
algo16[1] = 65;
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
/* Other algo16 is fine */
|
||||
memset(algo16, 0xFF, 16);
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
|
||||
/* NULL aux_rand argument is allowed. */
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
}
|
||||
|
||||
void test_schnorrsig_api(void) {
|
||||
unsigned char sk1[32];
|
||||
unsigned char sk2[32];
|
||||
unsigned char sk3[32];
|
||||
unsigned char msg[32];
|
||||
secp256k1_keypair keypairs[3];
|
||||
secp256k1_keypair invalid_keypair = { 0 };
|
||||
secp256k1_xonly_pubkey pk[3];
|
||||
secp256k1_xonly_pubkey zero_pk;
|
||||
unsigned char sig[64];
|
||||
|
||||
/** setup **/
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
int ecount;
|
||||
|
||||
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
|
||||
secp256k1_testrand256(sk1);
|
||||
secp256k1_testrand256(sk2);
|
||||
secp256k1_testrand256(sk3);
|
||||
secp256k1_testrand256(msg);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypairs[0], sk1) == 1);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypairs[1], sk2) == 1);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypairs[2], sk3) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[0], NULL, &keypairs[0]) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[1], NULL, &keypairs[1]) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[2], NULL, &keypairs[2]) == 1);
|
||||
memset(&zero_pk, 0, sizeof(zero_pk));
|
||||
|
||||
/** main test body **/
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL, NULL) == 0);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(none, sig, msg, &pk[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, &pk[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &pk[0]) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, &pk[0]) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(vrfy);
|
||||
secp256k1_context_destroy(both);
|
||||
}
|
||||
|
||||
/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the
|
||||
* expected state. */
|
||||
void test_schnorrsig_sha256_tagged(void) {
|
||||
char tag[17] = "BIP0340/challenge";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_optimized;
|
||||
|
||||
secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag));
|
||||
secp256k1_schnorrsig_sha256_tagged(&sha_optimized);
|
||||
test_sha256_eq(&sha, &sha_optimized);
|
||||
}
|
||||
|
||||
/* Helper function for schnorrsig_bip_vectors
|
||||
* Signs the message and checks that it's the same as expected_sig. */
|
||||
void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg, const unsigned char *expected_sig) {
|
||||
unsigned char sig[64];
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_xonly_pubkey pk, pk_expected;
|
||||
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, aux_rand));
|
||||
CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized));
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
|
||||
CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0);
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &pk));
|
||||
}
|
||||
|
||||
/* Helper function for schnorrsig_bip_vectors
|
||||
* Checks that both verify and verify_batch (TODO) return the same value as expected. */
|
||||
void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) {
|
||||
secp256k1_xonly_pubkey pk;
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized));
|
||||
CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk));
|
||||
}
|
||||
|
||||
/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See
|
||||
* https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */
|
||||
void test_schnorrsig_bip_vectors(void) {
|
||||
{
|
||||
/* Test vector 0 */
|
||||
const unsigned char sk[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
|
||||
};
|
||||
const unsigned char pk[32] = {
|
||||
0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10,
|
||||
0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29,
|
||||
0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0,
|
||||
0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9
|
||||
};
|
||||
unsigned char aux_rand[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0xE9, 0x07, 0x83, 0x1F, 0x80, 0x84, 0x8D, 0x10,
|
||||
0x69, 0xA5, 0x37, 0x1B, 0x40, 0x24, 0x10, 0x36,
|
||||
0x4B, 0xDF, 0x1C, 0x5F, 0x83, 0x07, 0xB0, 0x08,
|
||||
0x4C, 0x55, 0xF1, 0xCE, 0x2D, 0xCA, 0x82, 0x15,
|
||||
0x25, 0xF6, 0x6A, 0x4A, 0x85, 0xEA, 0x8B, 0x71,
|
||||
0xE4, 0x82, 0xA7, 0x4F, 0x38, 0x2D, 0x2C, 0xE5,
|
||||
0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47,
|
||||
0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
|
||||
}
|
||||
{
|
||||
/* Test vector 1 */
|
||||
const unsigned char sk[32] = {
|
||||
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A,
|
||||
0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7,
|
||||
0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56,
|
||||
0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF
|
||||
};
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
unsigned char aux_rand[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x68, 0x96, 0xBD, 0x60, 0xEE, 0xAE, 0x29, 0x6D,
|
||||
0xB4, 0x8A, 0x22, 0x9F, 0xF7, 0x1D, 0xFE, 0x07,
|
||||
0x1B, 0xDE, 0x41, 0x3E, 0x6D, 0x43, 0xF9, 0x17,
|
||||
0xDC, 0x8D, 0xCF, 0x8C, 0x78, 0xDE, 0x33, 0x41,
|
||||
0x89, 0x06, 0xD1, 0x1A, 0xC9, 0x76, 0xAB, 0xCC,
|
||||
0xB2, 0x0B, 0x09, 0x12, 0x92, 0xBF, 0xF4, 0xEA,
|
||||
0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C,
|
||||
0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
|
||||
}
|
||||
{
|
||||
/* Test vector 2 */
|
||||
const unsigned char sk[32] = {
|
||||
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
||||
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9
|
||||
};
|
||||
const unsigned char pk[32] = {
|
||||
0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13,
|
||||
0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC,
|
||||
0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9,
|
||||
0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8
|
||||
};
|
||||
unsigned char aux_rand[32] = {
|
||||
0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE,
|
||||
0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC,
|
||||
0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C,
|
||||
0x4B, 0x0B, 0x62, 0xD7, 0x98, 0xE6, 0xD9, 0x06
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x7E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A,
|
||||
0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D,
|
||||
0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33,
|
||||
0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x58, 0x31, 0xAA, 0xEE, 0xD7, 0xB4, 0x4B, 0xB7,
|
||||
0x4E, 0x5E, 0xAB, 0x94, 0xBA, 0x9D, 0x42, 0x94,
|
||||
0xC4, 0x9B, 0xCF, 0x2A, 0x60, 0x72, 0x8D, 0x8B,
|
||||
0x4C, 0x20, 0x0F, 0x50, 0xDD, 0x31, 0x3C, 0x1B,
|
||||
0xAB, 0x74, 0x58, 0x79, 0xA5, 0xAD, 0x95, 0x4A,
|
||||
0x72, 0xC4, 0x5A, 0x91, 0xC3, 0xA5, 0x1D, 0x3C,
|
||||
0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E,
|
||||
0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
|
||||
}
|
||||
{
|
||||
/* Test vector 3 */
|
||||
const unsigned char sk[32] = {
|
||||
0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81,
|
||||
0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0,
|
||||
0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54,
|
||||
0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10
|
||||
};
|
||||
const unsigned char pk[32] = {
|
||||
0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25,
|
||||
0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD,
|
||||
0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A,
|
||||
0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17
|
||||
};
|
||||
unsigned char aux_rand[32] = {
|
||||
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, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
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, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x7E, 0xB0, 0x50, 0x97, 0x57, 0xE2, 0x46, 0xF1,
|
||||
0x94, 0x49, 0x88, 0x56, 0x51, 0x61, 0x1C, 0xB9,
|
||||
0x65, 0xEC, 0xC1, 0xA1, 0x87, 0xDD, 0x51, 0xB6,
|
||||
0x4F, 0xDA, 0x1E, 0xDC, 0x96, 0x37, 0xD5, 0xEC,
|
||||
0x97, 0x58, 0x2B, 0x9C, 0xB1, 0x3D, 0xB3, 0x93,
|
||||
0x37, 0x05, 0xB3, 0x2B, 0xA9, 0x82, 0xAF, 0x5A,
|
||||
0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27,
|
||||
0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
|
||||
}
|
||||
{
|
||||
/* Test vector 4 */
|
||||
const unsigned char pk[32] = {
|
||||
0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12,
|
||||
0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83,
|
||||
0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA,
|
||||
0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2,
|
||||
0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24,
|
||||
0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B,
|
||||
0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F,
|
||||
0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28,
|
||||
0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63,
|
||||
0x76, 0xAF, 0xB1, 0x54, 0x8A, 0xF6, 0x03, 0xB3,
|
||||
0xEB, 0x45, 0xC9, 0xF8, 0x20, 0x7D, 0xEE, 0x10,
|
||||
0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93,
|
||||
0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
|
||||
}
|
||||
{
|
||||
/* Test vector 5 */
|
||||
const unsigned char pk[32] = {
|
||||
0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50,
|
||||
0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21,
|
||||
0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87,
|
||||
0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34
|
||||
};
|
||||
secp256k1_xonly_pubkey pk_parsed;
|
||||
/* No need to check the signature of the test vector as parsing the pubkey already fails */
|
||||
CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
|
||||
}
|
||||
{
|
||||
/* Test vector 6 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0xFF, 0xF9, 0x7B, 0xD5, 0x75, 0x5E, 0xEE, 0xA4,
|
||||
0x20, 0x45, 0x3A, 0x14, 0x35, 0x52, 0x35, 0xD3,
|
||||
0x82, 0xF6, 0x47, 0x2F, 0x85, 0x68, 0xA1, 0x8B,
|
||||
0x2F, 0x05, 0x7A, 0x14, 0x60, 0x29, 0x75, 0x56,
|
||||
0x3C, 0xC2, 0x79, 0x44, 0x64, 0x0A, 0xC6, 0x07,
|
||||
0xCD, 0x10, 0x7A, 0xE1, 0x09, 0x23, 0xD9, 0xEF,
|
||||
0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E,
|
||||
0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 7 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x1F, 0xA6, 0x2E, 0x33, 0x1E, 0xDB, 0xC2, 0x1C,
|
||||
0x39, 0x47, 0x92, 0xD2, 0xAB, 0x11, 0x00, 0xA7,
|
||||
0xB4, 0x32, 0xB0, 0x13, 0xDF, 0x3F, 0x6F, 0xF4,
|
||||
0xF9, 0x9F, 0xCB, 0x33, 0xE0, 0xE1, 0x51, 0x5F,
|
||||
0x28, 0x89, 0x0B, 0x3E, 0xDB, 0x6E, 0x71, 0x89,
|
||||
0xB6, 0x30, 0x44, 0x8B, 0x51, 0x5C, 0xE4, 0xF8,
|
||||
0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35,
|
||||
0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 8 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
|
||||
0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
|
||||
0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
|
||||
0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
|
||||
0x96, 0x17, 0x64, 0xB3, 0xAA, 0x9B, 0x2F, 0xFC,
|
||||
0xB6, 0xEF, 0x94, 0x7B, 0x68, 0x87, 0xA2, 0x26,
|
||||
0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C,
|
||||
0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 9 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x12, 0x3D, 0xDA, 0x83, 0x28, 0xAF, 0x9C, 0x23,
|
||||
0xA9, 0x4C, 0x1F, 0xEE, 0xCF, 0xD1, 0x23, 0xBA,
|
||||
0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC,
|
||||
0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 10 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x76, 0x15, 0xFB, 0xAF, 0x5A, 0xE2, 0x88, 0x64,
|
||||
0x01, 0x3C, 0x09, 0x97, 0x42, 0xDE, 0xAD, 0xB4,
|
||||
0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9,
|
||||
0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 11 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A,
|
||||
0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB,
|
||||
0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7,
|
||||
0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D,
|
||||
0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
|
||||
0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
|
||||
0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
|
||||
0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 12 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
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,
|
||||
0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
|
||||
0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
|
||||
0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
|
||||
0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 13 */
|
||||
const unsigned char pk[32] = {
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
};
|
||||
const unsigned char msg[32] = {
|
||||
0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
|
||||
0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
|
||||
0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
|
||||
0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
|
||||
};
|
||||
const unsigned char sig[64] = {
|
||||
0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
|
||||
0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
|
||||
0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
|
||||
0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
|
||||
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
|
||||
};
|
||||
test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
|
||||
}
|
||||
{
|
||||
/* Test vector 14 */
|
||||
const unsigned char pk[32] = {
|
||||
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, 0x30
|
||||
};
|
||||
secp256k1_xonly_pubkey pk_parsed;
|
||||
/* No need to check the signature of the test vector as parsing the pubkey already fails */
|
||||
CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
|
||||
}
|
||||
}
|
||||
|
||||
/* Nonce function that returns constant 0 */
|
||||
static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
|
||||
(void) msg32;
|
||||
(void) key32;
|
||||
(void) xonly_pk32;
|
||||
(void) algo16;
|
||||
(void) data;
|
||||
(void) nonce32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nonce function that sets nonce to 0 */
|
||||
static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
|
||||
(void) msg32;
|
||||
(void) key32;
|
||||
(void) xonly_pk32;
|
||||
(void) algo16;
|
||||
(void) data;
|
||||
|
||||
memset(nonce32, 0, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Nonce function that sets nonce to 0xFF...0xFF */
|
||||
static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
|
||||
(void) msg32;
|
||||
(void) key32;
|
||||
(void) xonly_pk32;
|
||||
(void) algo16;
|
||||
(void) data;
|
||||
|
||||
memset(nonce32, 0xFF, 32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void test_schnorrsig_sign(void) {
|
||||
unsigned char sk[32];
|
||||
secp256k1_keypair keypair;
|
||||
const unsigned char msg[32] = "this is a msg for a schnorrsig..";
|
||||
unsigned char sig[64];
|
||||
unsigned char zeros64[64] = { 0 };
|
||||
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
|
||||
|
||||
/* Test different nonce functions */
|
||||
memset(sig, 1, sizeof(sig));
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_failing, NULL) == 0);
|
||||
CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
|
||||
memset(&sig, 1, sizeof(sig));
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_0, NULL) == 0);
|
||||
CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_overflowing, NULL) == 1);
|
||||
CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) != 0);
|
||||
}
|
||||
|
||||
#define N_SIGS 3
|
||||
/* Creates N_SIGS valid signatures and verifies them with verify and
|
||||
* verify_batch (TODO). Then flips some bits and checks that verification now
|
||||
* fails. */
|
||||
void test_schnorrsig_sign_verify(void) {
|
||||
unsigned char sk[32];
|
||||
unsigned char msg[N_SIGS][32];
|
||||
unsigned char sig[N_SIGS][64];
|
||||
size_t i;
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_xonly_pubkey pk;
|
||||
secp256k1_scalar s;
|
||||
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
|
||||
|
||||
for (i = 0; i < N_SIGS; i++) {
|
||||
secp256k1_testrand256(msg[i]);
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL));
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], &pk));
|
||||
}
|
||||
|
||||
{
|
||||
/* Flip a few bits in the signature and in the message and check that
|
||||
* verify and verify_batch (TODO) fail */
|
||||
size_t sig_idx = secp256k1_testrand_int(N_SIGS);
|
||||
size_t byte_idx = secp256k1_testrand_int(32);
|
||||
unsigned char xorbyte = secp256k1_testrand_int(254)+1;
|
||||
sig[sig_idx][byte_idx] ^= xorbyte;
|
||||
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
|
||||
sig[sig_idx][byte_idx] ^= xorbyte;
|
||||
|
||||
byte_idx = secp256k1_testrand_int(32);
|
||||
sig[sig_idx][32+byte_idx] ^= xorbyte;
|
||||
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
|
||||
sig[sig_idx][32+byte_idx] ^= xorbyte;
|
||||
|
||||
byte_idx = secp256k1_testrand_int(32);
|
||||
msg[sig_idx][byte_idx] ^= xorbyte;
|
||||
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
|
||||
msg[sig_idx][byte_idx] ^= xorbyte;
|
||||
|
||||
/* Check that above bitflips have been reversed correctly */
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
|
||||
}
|
||||
|
||||
/* Test overflowing s */
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
|
||||
memset(&sig[0][32], 0xFF, 32);
|
||||
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
|
||||
|
||||
/* Test negative s */
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
|
||||
secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
secp256k1_scalar_get_b32(&sig[0][32], &s);
|
||||
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
|
||||
}
|
||||
#undef N_SIGS
|
||||
|
||||
void test_schnorrsig_taproot(void) {
|
||||
unsigned char sk[32];
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_xonly_pubkey internal_pk;
|
||||
unsigned char internal_pk_bytes[32];
|
||||
secp256k1_xonly_pubkey output_pk;
|
||||
unsigned char output_pk_bytes[32];
|
||||
unsigned char tweak[32];
|
||||
int pk_parity;
|
||||
unsigned char msg[32];
|
||||
unsigned char sig[64];
|
||||
|
||||
/* Create output key */
|
||||
secp256k1_testrand256(sk);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
|
||||
/* In actual taproot the tweak would be hash of internal_pk */
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, tweak, &internal_pk) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1);
|
||||
|
||||
/* Key spend */
|
||||
secp256k1_testrand256(msg);
|
||||
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
|
||||
/* Verify key spend */
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1);
|
||||
CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &output_pk) == 1);
|
||||
|
||||
/* Script spend */
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1);
|
||||
/* Verify script spend */
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1);
|
||||
}
|
||||
|
||||
void run_schnorrsig_tests(void) {
|
||||
int i;
|
||||
run_nonce_function_bip340_tests();
|
||||
|
||||
test_schnorrsig_api();
|
||||
test_schnorrsig_sha256_tagged();
|
||||
test_schnorrsig_bip_vectors();
|
||||
for (i = 0; i < count; i++) {
|
||||
test_schnorrsig_sign();
|
||||
test_schnorrsig_sign_verify();
|
||||
}
|
||||
test_schnorrsig_taproot();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -9,11 +9,20 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined HAVE_CONFIG_H
|
||||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#include "include/secp256k1_rangeproof.h"
|
||||
#include "include/secp256k1_surjectionproof.h"
|
||||
#include "modules/rangeproof/borromean.h"
|
||||
#include "modules/surjection/surjection_impl.h"
|
||||
#include "hash.h"
|
||||
#include "include/secp256k1_rangeproof.h"
|
||||
#include "include/secp256k1_surjectionproof.h"
|
||||
|
||||
#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE
|
||||
#undef SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS
|
||||
#define SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS 16
|
||||
#endif
|
||||
|
||||
static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count) {
|
||||
size_t ret = 0;
|
||||
|
|
@ -35,6 +44,9 @@ static size_t secp256k1_count_bits_set(const unsigned char* data, size_t count)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE
|
||||
static
|
||||
#endif
|
||||
int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surjectionproof *proof, const unsigned char *input, size_t inputlen) {
|
||||
size_t n_inputs;
|
||||
size_t signature_len;
|
||||
|
|
@ -55,6 +67,15 @@ int secp256k1_surjectionproof_parse(const secp256k1_context* ctx, secp256k1_surj
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Check that the bitvector of used inputs is of the claimed
|
||||
* length; i.e. the final byte has no "padding bits" set */
|
||||
if (n_inputs % 8 != 0) {
|
||||
const unsigned char padding_mask = (~0U) << (n_inputs % 8);
|
||||
if ((input[2 + (n_inputs + 7) / 8 - 1] & padding_mask) != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
signature_len = 32 * (1 + secp256k1_count_bits_set(&input[2], (n_inputs + 7) / 8));
|
||||
if (inputlen != 2 + (n_inputs + 7) / 8 + signature_len) {
|
||||
return 0;
|
||||
|
|
@ -151,6 +172,48 @@ static size_t secp256k1_surjectionproof_csprng_next(secp256k1_surjectionproof_cs
|
|||
}
|
||||
}
|
||||
|
||||
/* While '_allocate_initialized' may be a wordy suffix for this function, and '_create'
|
||||
* may have been more appropriate, '_create' could be confused with '_generate',
|
||||
* as the meanings for the words are close. Therefore, more wordy, but less
|
||||
* ambiguous suffix was chosen. */
|
||||
int secp256k1_surjectionproof_allocate_initialized(const secp256k1_context* ctx, secp256k1_surjectionproof** proof_out_p, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) {
|
||||
int ret = 0;
|
||||
secp256k1_surjectionproof* proof;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
|
||||
ARG_CHECK(proof_out_p != NULL);
|
||||
*proof_out_p = 0;
|
||||
|
||||
proof = (secp256k1_surjectionproof*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_surjectionproof));
|
||||
if (proof != NULL) {
|
||||
ret = secp256k1_surjectionproof_initialize(ctx, proof, input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, fixed_output_tag, n_max_iterations, random_seed32);
|
||||
if (ret) {
|
||||
*proof_out_p = proof;
|
||||
}
|
||||
else {
|
||||
free(proof);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* secp256k1_surjectionproof structure may also be allocated on the stack,
|
||||
* and initialized explicitly via secp256k1_surjectionproof_initialize().
|
||||
* Supplying stack-allocated struct to _destroy() will result in calling
|
||||
* free() with the pointer that points at the stack, with disasterous
|
||||
* consequences. Thus, it is not advised to mix heap- and stack-allocating
|
||||
* approaches to working with this struct. It is possible to detect this
|
||||
* situation by using additional field in the struct that can be set to
|
||||
* special value depending on the allocation path, and check it here.
|
||||
* But currently, it is not seen as big enough concern to warrant this extra code .*/
|
||||
void secp256k1_surjectionproof_destroy(secp256k1_surjectionproof* proof) {
|
||||
if (proof != NULL) {
|
||||
VERIFY_CHECK(proof->n_inputs <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
|
||||
free(proof);
|
||||
}
|
||||
}
|
||||
|
||||
int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1_surjectionproof* proof, size_t *input_index, const secp256k1_fixed_asset_tag* fixed_input_tags, const size_t n_input_tags, const size_t n_input_tags_to_use, const secp256k1_fixed_asset_tag* fixed_output_tag, const size_t n_max_iterations, const unsigned char *random_seed32) {
|
||||
secp256k1_surjectionproof_csprng csprng;
|
||||
size_t n_iterations = 0;
|
||||
|
|
@ -162,6 +225,7 @@ int secp256k1_surjectionproof_initialize(const secp256k1_context* ctx, secp256k1
|
|||
ARG_CHECK(fixed_output_tag != NULL);
|
||||
ARG_CHECK(random_seed32 != NULL);
|
||||
ARG_CHECK(n_input_tags <= SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
|
||||
ARG_CHECK(n_input_tags_to_use <= SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
|
||||
ARG_CHECK(n_input_tags_to_use <= n_input_tags);
|
||||
(void) ctx;
|
||||
|
||||
|
|
@ -219,10 +283,8 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s
|
|||
size_t n_total_pubkeys;
|
||||
size_t n_used_pubkeys;
|
||||
size_t ring_input_index = 0;
|
||||
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_ge output;
|
||||
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS];
|
||||
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS];
|
||||
unsigned char msg32[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
|
|
@ -261,17 +323,14 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s
|
|||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_generator_load(&output, ephemeral_output_tag);
|
||||
for (i = 0; i < n_total_pubkeys; i++) {
|
||||
secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]);
|
||||
if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, input_index, &ring_input_index) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, input_index, &ring_input_index);
|
||||
|
||||
/* Produce signature */
|
||||
rsizes[0] = (int) n_used_pubkeys;
|
||||
indices[0] = (int) ring_input_index;
|
||||
secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output);
|
||||
secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag);
|
||||
if (secp256k1_surjection_genrand(borromean_s, n_used_pubkeys, &blinding_key) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -289,15 +348,16 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef USE_REDUCED_SURJECTION_PROOF_SIZE
|
||||
static
|
||||
#endif
|
||||
int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* ephemeral_input_tags, size_t n_ephemeral_input_tags, const secp256k1_generator* ephemeral_output_tag) {
|
||||
size_t rsizes[1]; /* array needed for borromean sig API */
|
||||
size_t i;
|
||||
size_t n_total_pubkeys;
|
||||
size_t n_used_pubkeys;
|
||||
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_ge inputs[SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS];
|
||||
secp256k1_ge output;
|
||||
secp256k1_gej ring_pubkeys[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS];
|
||||
secp256k1_scalar borromean_s[SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS];
|
||||
unsigned char msg32[32];
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
|
|
@ -313,12 +373,12 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256
|
|||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_generator_load(&output, ephemeral_output_tag);
|
||||
for (i = 0; i < n_total_pubkeys; i++) {
|
||||
secp256k1_generator_load(&inputs[i], &ephemeral_input_tags[i]);
|
||||
/* Reject proofs with too many used inputs in USE_REDUCED_SURJECTION_PROOF_SIZE mode */
|
||||
if (n_used_pubkeys > SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, inputs, n_total_pubkeys, proof->used_inputs, &output, 0, NULL) == 0) {
|
||||
if (secp256k1_surjection_compute_public_keys(ring_pubkeys, n_used_pubkeys, ephemeral_input_tags, n_total_pubkeys, proof->used_inputs, ephemeral_output_tag, 0, NULL) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +391,7 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
secp256k1_surjection_genmessage(msg32, inputs, n_total_pubkeys, &output);
|
||||
secp256k1_surjection_genmessage(msg32, ephemeral_input_tags, n_total_pubkeys, ephemeral_output_tag);
|
||||
return secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include "scalar.h"
|
||||
#include "hash.h"
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, secp256k1_ge *ephemeral_input_tags, size_t n_input_tags, secp256k1_ge *ephemeral_output_tag) {
|
||||
SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg32, const secp256k1_generator *ephemeral_input_tags, size_t n_input_tags, const secp256k1_generator *ephemeral_output_tag) {
|
||||
/* compute message */
|
||||
size_t i;
|
||||
unsigned char pk_ser[33];
|
||||
|
|
@ -24,12 +24,12 @@ SECP256K1_INLINE static void secp256k1_surjection_genmessage(unsigned char *msg3
|
|||
|
||||
secp256k1_sha256_initialize(&sha256_en);
|
||||
for (i = 0; i < n_input_tags; i++) {
|
||||
secp256k1_eckey_pubkey_serialize(&ephemeral_input_tags[i], pk_ser, &pk_len, 1);
|
||||
assert(pk_len == sizeof(pk_ser));
|
||||
pk_ser[0] = 2 + (ephemeral_input_tags[i].data[63] & 1);
|
||||
memcpy(&pk_ser[1], &ephemeral_input_tags[i].data[0], 32);
|
||||
secp256k1_sha256_write(&sha256_en, pk_ser, pk_len);
|
||||
}
|
||||
secp256k1_eckey_pubkey_serialize(ephemeral_output_tag, pk_ser, &pk_len, 1);
|
||||
assert(pk_len == sizeof(pk_ser));
|
||||
pk_ser[0] = 2 + (ephemeral_output_tag->data[63] & 1);
|
||||
memcpy(&pk_ser[1], &ephemeral_output_tag->data[0], 32);
|
||||
secp256k1_sha256_write(&sha256_en, pk_ser, pk_len);
|
||||
secp256k1_sha256_finalize(&sha256_en, msg32);
|
||||
}
|
||||
|
|
@ -61,24 +61,29 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si
|
|||
return 1;
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_ge *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_ge *output_tag, size_t input_index, size_t *ring_input_index) {
|
||||
SECP256K1_INLINE static int secp256k1_surjection_compute_public_keys(secp256k1_gej *pubkeys, size_t n_pubkeys, const secp256k1_generator *input_tags, size_t n_input_tags, const unsigned char *used_tags, const secp256k1_generator *output_tag, size_t input_index, size_t *ring_input_index) {
|
||||
size_t i;
|
||||
size_t j = 0;
|
||||
for (i = 0; i < n_input_tags; i++) {
|
||||
if (used_tags[i / 8] & (1 << (i % 8))) {
|
||||
secp256k1_ge tmpge;
|
||||
secp256k1_ge_neg(&tmpge, &input_tags[i]);
|
||||
secp256k1_generator_load(&tmpge, &input_tags[i]);
|
||||
secp256k1_ge_neg(&tmpge, &tmpge);
|
||||
|
||||
VERIFY_CHECK(j < SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
|
||||
VERIFY_CHECK(j < n_pubkeys);
|
||||
secp256k1_gej_set_ge(&pubkeys[j], &tmpge);
|
||||
secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], output_tag, NULL);
|
||||
|
||||
secp256k1_generator_load(&tmpge, output_tag);
|
||||
secp256k1_gej_add_ge_var(&pubkeys[j], &pubkeys[j], &tmpge, NULL);
|
||||
if (ring_input_index != NULL && input_index == i) {
|
||||
*ring_input_index = j;
|
||||
}
|
||||
j++;
|
||||
if (j > n_pubkeys) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Caller needs to ensure that the number of set bits in used_tags (which we counted in j) equals n_pubkeys. */
|
||||
VERIFY_CHECK(j == n_pubkeys);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@ static void test_surjectionproof_api(void) {
|
|||
unsigned char serialized_proof[SECP256K1_SURJECTIONPROOF_SERIALIZATION_BYTES_MAX];
|
||||
size_t serialized_len;
|
||||
secp256k1_surjectionproof proof;
|
||||
secp256k1_surjectionproof* proof_on_heap;
|
||||
size_t n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]);
|
||||
size_t input_index;
|
||||
int32_t ecount = 0;
|
||||
size_t i;
|
||||
|
||||
secp256k1_rand256(seed);
|
||||
secp256k1_testrand256(seed);
|
||||
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
|
|
@ -44,14 +45,54 @@ static void test_surjectionproof_api(void) {
|
|||
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
|
||||
for (i = 0; i < n_inputs; i++) {
|
||||
secp256k1_rand256(input_blinding_key[i]);
|
||||
secp256k1_rand256(fixed_input_tags[i].data);
|
||||
secp256k1_testrand256(input_blinding_key[i]);
|
||||
secp256k1_testrand256(fixed_input_tags[i].data);
|
||||
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[i], fixed_input_tags[i].data, input_blinding_key[i]));
|
||||
}
|
||||
secp256k1_rand256(output_blinding_key);
|
||||
secp256k1_testrand256(output_blinding_key);
|
||||
memcpy(&fixed_output_tag, &fixed_input_tags[0], sizeof(fixed_input_tags[0]));
|
||||
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, output_blinding_key));
|
||||
|
||||
/* check allocate_initialized */
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) != 0);
|
||||
CHECK(proof_on_heap != 0);
|
||||
secp256k1_surjectionproof_destroy(proof_on_heap);
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, NULL, &input_index, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, NULL, fixed_input_tags, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, NULL, n_inputs, 3, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS + 1, 3, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs, &fixed_input_tags[0], 100, seed) != 0);
|
||||
CHECK(proof_on_heap != 0);
|
||||
secp256k1_surjectionproof_destroy(proof_on_heap);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, n_inputs + 1, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 3, NULL, 100, seed) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK((secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 0, seed) & 1) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_surjectionproof_allocate_initialized(none, &proof_on_heap, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, NULL) == 0);
|
||||
CHECK(proof_on_heap == 0);
|
||||
CHECK(ecount == 7);
|
||||
|
||||
/* we are now going to test essentially the same functions, just without heap allocation.
|
||||
* reset ecount. */
|
||||
ecount = 0;
|
||||
|
||||
/* check initialize */
|
||||
CHECK(secp256k1_surjectionproof_initialize(none, &proof, &input_index, fixed_input_tags, n_inputs, 0, &fixed_input_tags[0], 100, seed) == 0);
|
||||
CHECK(ecount == 0);
|
||||
|
|
@ -172,10 +213,10 @@ static void test_input_selection(size_t n_inputs) {
|
|||
const size_t max_n_inputs = sizeof(fixed_input_tags) / sizeof(fixed_input_tags[0]) - 1;
|
||||
|
||||
CHECK(n_inputs < max_n_inputs);
|
||||
secp256k1_rand256(seed);
|
||||
secp256k1_testrand256(seed);
|
||||
|
||||
for (i = 0; i < n_inputs + 1; i++) {
|
||||
secp256k1_rand256(fixed_input_tags[i].data);
|
||||
secp256k1_testrand256(fixed_input_tags[i].data);
|
||||
}
|
||||
|
||||
/* cannot match output when told to use zero keys */
|
||||
|
|
@ -240,7 +281,7 @@ static void test_input_selection_distribution_helper(const secp256k1_fixed_asset
|
|||
used_inputs[i] = 0;
|
||||
}
|
||||
for(j = 0; j < 10000; j++) {
|
||||
secp256k1_rand256(seed);
|
||||
secp256k1_testrand256(seed);
|
||||
result = secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, fixed_input_tags, n_input_tags, n_input_tags_to_use, &fixed_input_tags[0], 64, seed);
|
||||
CHECK(result > 0);
|
||||
|
||||
|
|
@ -263,7 +304,7 @@ static void test_input_selection_distribution(void) {
|
|||
size_t used_inputs[4];
|
||||
|
||||
for (i = 0; i < n_inputs; i++) {
|
||||
secp256k1_rand256(fixed_input_tags[i].data);
|
||||
secp256k1_testrand256(fixed_input_tags[i].data);
|
||||
}
|
||||
|
||||
/* If there is one input tag to use, initialize must choose the one equal to fixed_output_tag. */
|
||||
|
|
@ -346,16 +387,16 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) {
|
|||
/* setup */
|
||||
CHECK(n_used <= n_inputs);
|
||||
CHECK(n_inputs < max_n_inputs);
|
||||
secp256k1_rand256(seed);
|
||||
secp256k1_testrand256(seed);
|
||||
|
||||
key_index = (((size_t) seed[0] << 8) + seed[1]) % n_inputs;
|
||||
|
||||
for (i = 0; i < n_inputs + 1; i++) {
|
||||
input_blinding_key[i] = malloc(32);
|
||||
secp256k1_rand256(input_blinding_key[i]);
|
||||
secp256k1_testrand256(input_blinding_key[i]);
|
||||
/* choose random fixed tag, except that for the output one copy from the key_index */
|
||||
if (i < n_inputs) {
|
||||
secp256k1_rand256(fixed_input_tags[i].data);
|
||||
secp256k1_testrand256(fixed_input_tags[i].data);
|
||||
} else {
|
||||
memcpy(&fixed_input_tags[i], &fixed_input_tags[key_index], sizeof(fixed_input_tags[i]));
|
||||
}
|
||||
|
|
@ -381,11 +422,12 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) {
|
|||
/* trailing garbage */
|
||||
memcpy(&serialized_proof_trailing, &serialized_proof, serialized_len);
|
||||
serialized_proof_trailing[serialized_len] = seed[0];
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len + 1) == 0);
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof_trailing, serialized_len + 1) == 0);
|
||||
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len));
|
||||
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]);
|
||||
CHECK(result == 1);
|
||||
|
||||
/* various fail cases */
|
||||
if (n_inputs > 1) {
|
||||
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs - 1]);
|
||||
|
|
@ -400,6 +442,15 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) {
|
|||
n_inputs += 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_inputs; i++) {
|
||||
/* flip bit */
|
||||
proof.used_inputs[i / 8] ^= (1 << (i % 8));
|
||||
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]);
|
||||
CHECK(result == 0);
|
||||
/* reset the bit */
|
||||
proof.used_inputs[i / 8] ^= (1 << (i % 8));
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
for (i = 0; i < n_inputs + 1; i++) {
|
||||
free(input_blinding_key[i]);
|
||||
|
|
@ -415,7 +466,6 @@ static void test_no_used_inputs_verify(void) {
|
|||
size_t n_ephemeral_input_tags = 1;
|
||||
secp256k1_generator ephemeral_output_tag;
|
||||
unsigned char blinding_key[32];
|
||||
secp256k1_ge inputs[1];
|
||||
secp256k1_ge output;
|
||||
secp256k1_sha256 sha256_e0;
|
||||
int result;
|
||||
|
|
@ -426,18 +476,17 @@ static void test_no_used_inputs_verify(void) {
|
|||
memset(proof.used_inputs, 0, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS / 8);
|
||||
|
||||
/* create different fixed input and output tags */
|
||||
secp256k1_rand256(fixed_input_tag.data);
|
||||
secp256k1_rand256(fixed_output_tag.data);
|
||||
secp256k1_testrand256(fixed_input_tag.data);
|
||||
secp256k1_testrand256(fixed_output_tag.data);
|
||||
|
||||
/* blind fixed output tags with random blinding key */
|
||||
secp256k1_rand256(blinding_key);
|
||||
secp256k1_testrand256(blinding_key);
|
||||
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_input_tags[0], fixed_input_tag.data, blinding_key));
|
||||
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_output_tag, fixed_output_tag.data, blinding_key));
|
||||
|
||||
/* create "borromean signature" which is just a hash of metadata (pubkeys, etc) in this case */
|
||||
secp256k1_generator_load(&output, &ephemeral_output_tag);
|
||||
secp256k1_generator_load(&inputs[0], &ephemeral_input_tags[0]);
|
||||
secp256k1_surjection_genmessage(proof.data, inputs, 1, &output);
|
||||
secp256k1_surjection_genmessage(proof.data, ephemeral_input_tags, 1, &ephemeral_output_tag);
|
||||
secp256k1_sha256_initialize(&sha256_e0);
|
||||
secp256k1_sha256_write(&sha256_e0, proof.data, 32);
|
||||
secp256k1_sha256_finalize(&sha256_e0, proof.data);
|
||||
|
|
@ -471,21 +520,164 @@ void test_bad_parse(void) {
|
|||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0);
|
||||
}
|
||||
|
||||
void test_fixed_vectors(void) {
|
||||
const unsigned char tag0_ser[] = {
|
||||
0x0a,
|
||||
0x1c, 0xa3, 0xdd, 0x12, 0x48, 0xdd, 0x4d, 0xd0, 0x04, 0x30, 0x47, 0x48, 0x75, 0xf5, 0xf5, 0xff,
|
||||
0x2a, 0xd5, 0x0d, 0x1d, 0x86, 0x2b, 0xa4, 0xa4, 0x2f, 0x46, 0xe9, 0xb4, 0x54, 0x21, 0xf0, 0x85
|
||||
};
|
||||
const unsigned char tag1_ser[] = {
|
||||
0x0a,
|
||||
0x09, 0x0d, 0x5a, 0xd4, 0xed, 0xae, 0x9c, 0x0c, 0x69, 0x79, 0xf3, 0x8d, 0x22, 0x03, 0x0a, 0x3d,
|
||||
0x38, 0xd4, 0x78, 0xe1, 0x03, 0x0d, 0x70, 0x57, 0xd9, 0x9a, 0x23, 0x03, 0xf0, 0x7f, 0xfb, 0xef
|
||||
};
|
||||
const unsigned char tag2_ser[] = {
|
||||
0x0a,
|
||||
0xfd, 0xed, 0xba, 0x15, 0x20, 0x8a, 0xb2, 0xaf, 0x0b, 0x76, 0x6d, 0xd2, 0x5f, 0xd4, 0x15, 0x11,
|
||||
0x90, 0xec, 0xcb, 0x3f, 0xcd, 0x08, 0xb5, 0x35, 0xd9, 0x24, 0x18, 0xb1, 0xd3, 0x47, 0x83, 0x54
|
||||
};
|
||||
const unsigned char tag3_ser[] = {
|
||||
0x0b,
|
||||
0x8b, 0x47, 0xca, 0xee, 0x20, 0x52, 0x17, 0xbf, 0xee, 0xcc, 0x84, 0xcd, 0x34, 0x32, 0x6c, 0x36,
|
||||
0xf1, 0xd9, 0x3f, 0xe1, 0x6f, 0x77, 0xfe, 0x89, 0x3e, 0x4a, 0xc8, 0x2a, 0x75, 0xfa, 0x2d, 0x36
|
||||
};
|
||||
const unsigned char tag4_ser[] = {
|
||||
0x0b,
|
||||
0x3c, 0x5c, 0xf4, 0x61, 0x45, 0xa8, 0x53, 0xc1, 0x64, 0x32, 0x0e, 0x92, 0x68, 0x52, 0xbd, 0x12,
|
||||
0xe9, 0x45, 0x31, 0xeb, 0x04, 0x4c, 0xf4, 0xe2, 0x9e, 0x9f, 0x60, 0x26, 0x50, 0xbf, 0xd6, 0x9f
|
||||
};
|
||||
const unsigned char output_tag_ser[] = {
|
||||
0x0b,
|
||||
0xf7, 0x3c, 0x03, 0xed, 0xae, 0x83, 0xa1, 0xa6, 0x94, 0x8c, 0xe3, 0xb8, 0x54, 0x02, 0xa8, 0xbd,
|
||||
0x66, 0xca, 0x28, 0xef, 0x44, 0xf5, 0x3a, 0xcb, 0xc7, 0x5b, 0x16, 0xac, 0xce, 0x29, 0x4b, 0xc6
|
||||
};
|
||||
|
||||
const unsigned char total1_used1[] = {
|
||||
0x01, 0x00, 0x01, 0x8e, 0x6b, 0x8d, 0x8b, 0x96, 0x29, 0x10, 0x29, 0xcb, 0xf8, 0x48, 0xd9, 0xc8,
|
||||
0x5b, 0x77, 0xdc, 0xdf, 0x16, 0x67, 0x19, 0xfe, 0x8d, 0xee, 0x8f, 0x56, 0x6f, 0x9c, 0xe9, 0xae,
|
||||
0xb9, 0xd9, 0x12, 0xb8, 0x95, 0x6c, 0xf1, 0x48, 0x07, 0x7d, 0x49, 0xe4, 0x3e, 0x7f, 0xc1, 0x2c,
|
||||
0xe2, 0xe1, 0x94, 0x10, 0xb1, 0xda, 0x86, 0x5f, 0xbc, 0x03, 0x59, 0xe1, 0x09, 0xd2, 0x1b, 0x18,
|
||||
0xce, 0x58, 0x15
|
||||
};
|
||||
const size_t total1_used1_len = sizeof(total1_used1);
|
||||
|
||||
const unsigned char total2_used1[] = {
|
||||
0x02, 0x00, 0x01, 0x35, 0x3a, 0x29, 0x4b, 0xe4, 0x99, 0xc6, 0xbf, 0x99, 0x4d, 0x6c, 0xc8, 0x18,
|
||||
0x14, 0xad, 0x10, 0x22, 0x3a, 0xb8, 0x1c, 0xb9, 0xc5, 0x77, 0xda, 0xe0, 0x8a, 0x71, 0x2d, 0x0d,
|
||||
0x8e, 0x80, 0xf5, 0x8d, 0x74, 0xf9, 0x01, 0x6b, 0x35, 0x88, 0xf4, 0x8e, 0x43, 0xa5, 0x9c, 0x0f,
|
||||
0x7e, 0x37, 0x86, 0x77, 0x44, 0x72, 0x7c, 0xaa, 0xff, 0x14, 0x5b, 0x7a, 0x42, 0x41, 0x75, 0xb2,
|
||||
0x5e, 0x3d, 0x6c
|
||||
};
|
||||
const size_t total2_used1_len = sizeof(total2_used1);
|
||||
|
||||
const unsigned char total3_used2[] = {
|
||||
0x03, 0x00, 0x03, 0xf2, 0x3f, 0xca, 0x49, 0x52, 0x05, 0xaf, 0x81, 0x83, 0x01, 0xd7, 0xf4, 0x92,
|
||||
0xc0, 0x50, 0xe3, 0x15, 0xfc, 0x94, 0xc1, 0x27, 0x10, 0xd7, 0x8f, 0x57, 0xb1, 0x23, 0xcf, 0x68,
|
||||
0x31, 0xf8, 0xcb, 0x58, 0x3d, 0xca, 0x2f, 0x7a, 0x3b, 0x0b, 0xb6, 0x10, 0x52, 0x94, 0xc8, 0x5f,
|
||||
0x0a, 0xf8, 0xca, 0x5d, 0x4c, 0x38, 0x44, 0x92, 0xb3, 0xc7, 0xe4, 0x46, 0x9f, 0x96, 0x64, 0xbd,
|
||||
0xd2, 0xda, 0x40, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe,
|
||||
0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90,
|
||||
0x4c, 0xa4, 0x91
|
||||
};
|
||||
const size_t total3_used2_len = sizeof(total3_used2);
|
||||
|
||||
const unsigned char total5_used3[] = {
|
||||
0x05, 0x00, 0x15, 0x36, 0x3b, 0x92, 0x97, 0x84, 0x25, 0x75, 0xd6, 0xa6, 0xaf, 0xb7, 0x32, 0x5b,
|
||||
0x2c, 0xf8, 0x31, 0xe2, 0x15, 0x3a, 0x9b, 0xb7, 0x20, 0x14, 0xc0, 0x67, 0x96, 0x7d, 0xa9, 0xc4,
|
||||
0xa2, 0xb4, 0x22, 0x57, 0x5f, 0xb8, 0x20, 0xf1, 0xe8, 0x82, 0xaf, 0xbc, 0x8a, 0xbc, 0x01, 0xc9,
|
||||
0x35, 0xf2, 0x7f, 0x6f, 0x0c, 0x0d, 0xba, 0x87, 0xa4, 0xc3, 0xec, 0x60, 0x54, 0x49, 0x35, 0xeb,
|
||||
0x1e, 0x48, 0x2c, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe,
|
||||
0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90,
|
||||
0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b,
|
||||
0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee,
|
||||
0x9c, 0xf5, 0xb9
|
||||
};
|
||||
const size_t total5_used3_len = sizeof(total5_used3);
|
||||
|
||||
const unsigned char total5_used5[] = {
|
||||
0x05, 0x00, 0x1f, 0xfd, 0xbb, 0xb6, 0xc2, 0x78, 0x82, 0xad, 0xe1, 0x66, 0x6d, 0x20, 0x4d, 0xfe,
|
||||
0x6b, 0xd2, 0x0b, 0x21, 0x6e, 0xa8, 0x5b, 0xc8, 0xe4, 0x88, 0x42, 0x11, 0x30, 0x3b, 0x6b, 0x02,
|
||||
0xc9, 0x7f, 0x44, 0x1c, 0xee, 0xd8, 0x37, 0x6a, 0xf8, 0xfd, 0xc8, 0x4b, 0x0b, 0xa1, 0x43, 0x1f,
|
||||
0x68, 0x77, 0x8d, 0x1b, 0xac, 0x9e, 0xc1, 0xc1, 0xda, 0x60, 0xa8, 0xcf, 0x10, 0x9d, 0x80, 0x07,
|
||||
0x90, 0x57, 0xb6, 0xdb, 0x63, 0x76, 0x87, 0x48, 0xdc, 0x55, 0x0b, 0x82, 0x9c, 0xa5, 0x96, 0xbe,
|
||||
0xe9, 0x0d, 0xe4, 0x98, 0x80, 0x8e, 0x58, 0x38, 0xdc, 0x13, 0x59, 0x1d, 0x5c, 0x8e, 0xda, 0x90,
|
||||
0x4c, 0xa4, 0x91, 0x5e, 0x8f, 0xcf, 0x2e, 0xc7, 0x5f, 0xfc, 0xca, 0x42, 0xd8, 0x80, 0xe4, 0x3b,
|
||||
0x90, 0xa5, 0xd2, 0x07, 0x7d, 0xd1, 0xc9, 0x5c, 0x69, 0xc2, 0xd7, 0xef, 0x8a, 0xae, 0x0a, 0xee,
|
||||
0x9c, 0xf5, 0xb9, 0x5a, 0xc8, 0x03, 0x8d, 0x4f, 0xe3, 0x1d, 0x79, 0x38, 0x5a, 0xfa, 0xe5, 0xa8,
|
||||
0x9d, 0x56, 0x77, 0xb3, 0xf9, 0xa8, 0x70, 0x46, 0x27, 0x26, 0x6c, 0x6e, 0x54, 0xaf, 0xf9, 0xd0,
|
||||
0x37, 0xa4, 0x86, 0x68, 0x8f, 0xac, 0x3e, 0x78, 0xaa, 0x3d, 0x83, 0x1a, 0xca, 0x05, 0xfe, 0x10,
|
||||
0x95, 0xa4, 0x6a, 0x10, 0xc6, 0x62, 0xf3, 0xf7, 0xf3, 0x4d, 0x0b, 0xd4, 0x94, 0xe5, 0x51, 0x6c,
|
||||
0x85, 0xd7, 0xc7
|
||||
};
|
||||
const size_t total5_used5_len = sizeof(total5_used5);
|
||||
|
||||
unsigned char bad[sizeof(total5_used5) + 32] = { 0 };
|
||||
|
||||
secp256k1_generator input_tags[5];
|
||||
secp256k1_generator output_tag;
|
||||
secp256k1_surjectionproof proof;
|
||||
|
||||
CHECK(secp256k1_generator_parse(ctx, &input_tags[0], tag0_ser));
|
||||
CHECK(secp256k1_generator_parse(ctx, &input_tags[1], tag1_ser));
|
||||
CHECK(secp256k1_generator_parse(ctx, &input_tags[2], tag2_ser));
|
||||
CHECK(secp256k1_generator_parse(ctx, &input_tags[3], tag3_ser));
|
||||
CHECK(secp256k1_generator_parse(ctx, &input_tags[4], tag4_ser));
|
||||
CHECK(secp256k1_generator_parse(ctx, &output_tag, output_tag_ser));
|
||||
|
||||
/* check 1-of-1 */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len));
|
||||
CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &output_tag));
|
||||
/* check 1-of-2 */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total2_used1, total2_used1_len));
|
||||
CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 2, &output_tag));
|
||||
/* check 2-of-3 */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total3_used2, total3_used2_len));
|
||||
CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 3, &output_tag));
|
||||
/* check 3-of-5 */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used3, total5_used3_len));
|
||||
CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag));
|
||||
/* check 5-of-5 */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used5_len));
|
||||
CHECK(secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 5, &output_tag));
|
||||
|
||||
/* check invalid length fails */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, total5_used5, total5_used3_len));
|
||||
/* check invalid keys fail */
|
||||
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, total1_used1, total1_used1_len));
|
||||
CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, &input_tags[1], 1, &output_tag));
|
||||
CHECK(!secp256k1_surjectionproof_verify(ctx, &proof, input_tags, 1, &input_tags[0]));
|
||||
|
||||
/* Try setting 6 bits on the total5-used-5; check that parsing fails */
|
||||
memcpy(bad, total5_used5, total5_used5_len);
|
||||
bad[2] = 0x3f; /* 0x1f -> 0x3f */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len));
|
||||
/* Correct for the length */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len + 32));
|
||||
/* Alternately just turn off one of the "legit" bits */
|
||||
bad[2] = 0x37; /* 0x1f -> 0x37 */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used5_len));
|
||||
|
||||
/* Similarly try setting 4 bits on the total5-used-3, with one bit out of range */
|
||||
memcpy(bad, total5_used3, total5_used3_len);
|
||||
bad[2] = 0x35; /* 0x15 -> 0x35 */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len));
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len + 32));
|
||||
bad[2] = 0x34; /* 0x15 -> 0x34 */
|
||||
CHECK(!secp256k1_surjectionproof_parse(ctx, &proof, bad, total5_used3_len));
|
||||
}
|
||||
|
||||
void run_surjection_tests(void) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
test_surjectionproof_api();
|
||||
}
|
||||
test_surjectionproof_api();
|
||||
test_fixed_vectors();
|
||||
|
||||
test_input_selection(0);
|
||||
test_input_selection(1);
|
||||
test_input_selection(5);
|
||||
test_input_selection(100);
|
||||
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
|
||||
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
|
||||
|
||||
test_input_selection_distribution();
|
||||
test_gen_verify(10, 3);
|
||||
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS);
|
||||
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
|
||||
test_no_used_inputs_verify();
|
||||
test_bad_serialize();
|
||||
test_bad_parse();
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ A less obvious scheme is to have a participant sign an arbitrary message with
|
|||
the sum of her key `P` and the whitelisted key `W`. Such a signature with the key
|
||||
`P + W` proves knowledge of either (a) discrete logarithms of both `P` and `W`;
|
||||
or (b) neither. This makes directly attacking participants' signing schemes much
|
||||
harder, but allows an attacker to whitelist arbitrary "garbage" keys by computing
|
||||
`W` as the difference between an attacker-controlled key and `P`. For Bitcoin,
|
||||
the effect of garbage keys is to "burn" stolen coins, destroying them.
|
||||
harder, but allows an attacker to whitelist arbitrary "cancellation" keys by
|
||||
computing `W` as the difference between an attacker-controlled key and `P`.
|
||||
Because to spend the funds the attacker must produce a signature with `W`, the
|
||||
coins will be unspendable until attacker and the legitimate participant owning
|
||||
`P` cooperate.
|
||||
|
||||
In an important sense, this "burning coins" attack is a good thing: it enables
|
||||
In an important sense, this "cancellation" attack is a good thing: it enables
|
||||
*offline delegation*. That is, the key `P` does not need to be available at the
|
||||
time of delegation. Instead, participants could choose `S = P + W`, sign with
|
||||
this to delegate, and only later compute the discrete logarithm of `W = P - S`.
|
||||
|
|
@ -47,7 +49,7 @@ the overall system security.
|
|||
|
||||
#### Signing with Tweaked-Difference-of-Keys
|
||||
|
||||
A modification of this scheme, which prevents this "garbage key" attack, is to
|
||||
A modification of this scheme, which prevents this "cancellation" attack, is to
|
||||
instead have participants sign some message with the key `P + H(W)W`, for `H`
|
||||
some random-oracle hash that maps group elements to scalars. This key, and its
|
||||
discrete logarithm, cannot be known until after `W` is chosen, so `W` cannot
|
||||
|
|
@ -60,8 +62,8 @@ delegation. However, we can get this back by introducing a new key, `P'`,
|
|||
and signing with the key `P + H(W + P')(W + P')`. This gives us the best
|
||||
of both worlds: `P'` does not need to be online to delegate, allowing it
|
||||
to be securely stored and preventing real-time attacks; `P` does need to
|
||||
be online, but its compromise only allows an attacker to whitelist "garbage
|
||||
keys", not attacker-controlled ones.
|
||||
be online, but its compromise only allows an attacker to whitelist keys he does
|
||||
not control alone.
|
||||
|
||||
### Our Scheme
|
||||
|
||||
|
|
@ -78,8 +80,8 @@ knows:
|
|||
1. The discrete logarithms of all of `W`, `P_i` and `Q_i`; or
|
||||
2. The discrete logarithm of `P_i` but of *neither* `W` nor `Q_i`.
|
||||
In other words, compromise of the online key `P_i` allows an attacker to whitelist
|
||||
"garbage keys" for which nobody knows the discrete logarithm; to whitelist an
|
||||
attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult
|
||||
"cancellation keys" for which the attacker alone does not know the discrete logarithm;
|
||||
to whitelist an attacker-controlled key, he must compromise both `P_i` and `Q_i`. This is difficult
|
||||
because by design, only the sum `S = W + Q_i` is used when signing; then by choosing
|
||||
`S` freely, a participant can delegate without the secret key to `Q_i` ever being online.
|
||||
(Later, when she wants to actually use `W`, she will need to compute its key as the
|
||||
|
|
|
|||
34
src/scalar.h
34
src/scalar.h
|
|
@ -8,6 +8,7 @@
|
|||
#define SECP256K1_SCALAR_H
|
||||
|
||||
#include "num.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined HAVE_CONFIG_H
|
||||
#include "libsecp256k1-config.h"
|
||||
|
|
@ -15,12 +16,12 @@
|
|||
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
#include "scalar_low.h"
|
||||
#elif defined(USE_SCALAR_4X64)
|
||||
#elif defined(SECP256K1_WIDEMUL_INT128)
|
||||
#include "scalar_4x64.h"
|
||||
#elif defined(USE_SCALAR_8X32)
|
||||
#elif defined(SECP256K1_WIDEMUL_INT64)
|
||||
#include "scalar_8x32.h"
|
||||
#else
|
||||
#error "Please select scalar implementation"
|
||||
#error "Please select wide multiplication implementation"
|
||||
#endif
|
||||
|
||||
/** Clear a scalar to prevent the leak of sensitive data. */
|
||||
|
|
@ -32,9 +33,17 @@ static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigne
|
|||
/** Access bits from a scalar. Not constant time. */
|
||||
static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count);
|
||||
|
||||
/** Set a scalar from a big endian byte array. */
|
||||
/** Set a scalar from a big endian byte array. The scalar will be reduced modulo group order `n`.
|
||||
* In: bin: pointer to a 32-byte array.
|
||||
* Out: r: scalar to be set.
|
||||
* overflow: non-zero if the scalar was bigger or equal to `n` before reduction, zero otherwise (can be NULL).
|
||||
*/
|
||||
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow);
|
||||
|
||||
/** Set a scalar from a big endian byte array and returns 1 if it is a valid
|
||||
* seckey and 0 otherwise. */
|
||||
static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin);
|
||||
|
||||
/** Set a scalar to an unsigned integer. */
|
||||
static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v);
|
||||
|
||||
|
|
@ -96,14 +105,19 @@ static void secp256k1_scalar_order_get_num(secp256k1_num *r);
|
|||
/** Compare two scalars. */
|
||||
static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/** Find r1 and r2 such that r1+r2*2^128 = a. */
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a);
|
||||
/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a);
|
||||
#endif
|
||||
/** Find r1 and r2 such that r1+r2*2^128 = k. */
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k);
|
||||
/** Find r1 and r2 such that r1+r2*lambda = k,
|
||||
* where r1 and r2 or their negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). */
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k);
|
||||
|
||||
/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */
|
||||
static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift);
|
||||
|
||||
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/
|
||||
static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag);
|
||||
|
||||
/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */
|
||||
static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx);
|
||||
|
||||
#endif /* SECP256K1_SCALAR_H */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define SECP256K1_SCALAR_REPR_IMPL_H
|
||||
|
||||
#include "scalar.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Limbs of the secp256k1 order. */
|
||||
#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL)
|
||||
|
|
@ -201,9 +202,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
c0 += tl; /* overflow is handled on the next line */ \
|
||||
th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
c1 += th; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
|
||||
c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
|
||||
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +217,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
c0 += tl; /* overflow is handled on the next line */ \
|
||||
th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
c1 += th; /* never overflows by contract (verified in the next line) */ \
|
||||
VERIFY_CHECK(c1 >= th); \
|
||||
}
|
||||
|
|
@ -230,16 +231,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \
|
||||
c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
|
||||
tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \
|
||||
th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
|
||||
c0 += tl2; /* overflow is handled on the next line */ \
|
||||
th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
|
||||
th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
|
||||
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
|
||||
c1 += th2; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
|
||||
}
|
||||
|
||||
|
|
@ -247,15 +248,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
#define sumadd(a) { \
|
||||
unsigned int over; \
|
||||
c0 += (a); /* overflow is handled on the next line */ \
|
||||
over = (c0 < (a)) ? 1 : 0; \
|
||||
over = (c0 < (a)); \
|
||||
c1 += over; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
|
||||
c2 += (c1 < over); /* never overflows by contract */ \
|
||||
}
|
||||
|
||||
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
|
||||
#define sumadd_fast(a) { \
|
||||
c0 += (a); /* overflow is handled on the next line */ \
|
||||
c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
|
||||
VERIFY_CHECK(c2 == 0); \
|
||||
}
|
||||
|
|
@ -385,7 +386,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
/* extract m6 */
|
||||
"movq %%r8, %q6\n"
|
||||
: "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6)
|
||||
: "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
|
||||
: "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
|
||||
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc");
|
||||
|
||||
/* Reduce 385 bits into 258. */
|
||||
|
|
@ -464,7 +465,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
/* extract p4 */
|
||||
"movq %%r9, %q4\n"
|
||||
: "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4)
|
||||
: "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
|
||||
: "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
|
||||
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc");
|
||||
|
||||
/* Reduce 258 bits into 256. */
|
||||
|
|
@ -510,7 +511,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
/* Extract c */
|
||||
"movq %%r9, %q0\n"
|
||||
: "=g"(c)
|
||||
: "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1)
|
||||
: "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
|
||||
: "rax", "rdx", "r8", "r9", "r10", "cc", "memory");
|
||||
#else
|
||||
uint128_t c;
|
||||
|
|
@ -921,18 +922,16 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
|
|||
secp256k1_scalar_reduce_512(r, l);
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
r1->d[0] = a->d[0];
|
||||
r1->d[1] = a->d[1];
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
|
||||
r1->d[0] = k->d[0];
|
||||
r1->d[1] = k->d[1];
|
||||
r1->d[2] = 0;
|
||||
r1->d[3] = 0;
|
||||
r2->d[0] = a->d[2];
|
||||
r2->d[1] = a->d[3];
|
||||
r2->d[0] = k->d[2];
|
||||
r2->d[1] = k->d[3];
|
||||
r2->d[2] = 0;
|
||||
r2->d[3] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0;
|
||||
|
|
@ -955,4 +954,102 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
|
|||
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1);
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
|
||||
uint64_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->d, sizeof(r->d));
|
||||
mask0 = flag + ~((uint64_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
|
||||
r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
|
||||
r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
|
||||
r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
|
||||
}
|
||||
|
||||
#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n)))
|
||||
#define QUARTERROUND(a,b,c,d) \
|
||||
a += b; d = ROTL32(d ^ a, 16); \
|
||||
c += d; b = ROTL32(b ^ c, 12); \
|
||||
a += b; d = ROTL32(d ^ a, 8); \
|
||||
c += d; b = ROTL32(b ^ c, 7);
|
||||
|
||||
#if defined(SECP256K1_BIG_ENDIAN)
|
||||
#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
|
||||
#elif defined(SECP256K1_LITTLE_ENDIAN)
|
||||
#define LE32(p) (p)
|
||||
#endif
|
||||
|
||||
static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) {
|
||||
size_t n;
|
||||
size_t over_count = 0;
|
||||
uint32_t seed32[8];
|
||||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
int over1, over2;
|
||||
|
||||
memcpy((void *) seed32, (const void *) seed, 32);
|
||||
do {
|
||||
x0 = 0x61707865;
|
||||
x1 = 0x3320646e;
|
||||
x2 = 0x79622d32;
|
||||
x3 = 0x6b206574;
|
||||
x4 = LE32(seed32[0]);
|
||||
x5 = LE32(seed32[1]);
|
||||
x6 = LE32(seed32[2]);
|
||||
x7 = LE32(seed32[3]);
|
||||
x8 = LE32(seed32[4]);
|
||||
x9 = LE32(seed32[5]);
|
||||
x10 = LE32(seed32[6]);
|
||||
x11 = LE32(seed32[7]);
|
||||
x12 = idx;
|
||||
x13 = idx >> 32;
|
||||
x14 = 0;
|
||||
x15 = over_count;
|
||||
|
||||
n = 10;
|
||||
while (n--) {
|
||||
QUARTERROUND(x0, x4, x8,x12)
|
||||
QUARTERROUND(x1, x5, x9,x13)
|
||||
QUARTERROUND(x2, x6,x10,x14)
|
||||
QUARTERROUND(x3, x7,x11,x15)
|
||||
QUARTERROUND(x0, x5,x10,x15)
|
||||
QUARTERROUND(x1, x6,x11,x12)
|
||||
QUARTERROUND(x2, x7, x8,x13)
|
||||
QUARTERROUND(x3, x4, x9,x14)
|
||||
}
|
||||
|
||||
x0 += 0x61707865;
|
||||
x1 += 0x3320646e;
|
||||
x2 += 0x79622d32;
|
||||
x3 += 0x6b206574;
|
||||
x4 += LE32(seed32[0]);
|
||||
x5 += LE32(seed32[1]);
|
||||
x6 += LE32(seed32[2]);
|
||||
x7 += LE32(seed32[3]);
|
||||
x8 += LE32(seed32[4]);
|
||||
x9 += LE32(seed32[5]);
|
||||
x10 += LE32(seed32[6]);
|
||||
x11 += LE32(seed32[7]);
|
||||
x12 += idx;
|
||||
x13 += idx >> 32;
|
||||
x14 += 0;
|
||||
x15 += over_count;
|
||||
|
||||
r1->d[3] = (((uint64_t) x0) << 32) | x1;
|
||||
r1->d[2] = (((uint64_t) x2) << 32) | x3;
|
||||
r1->d[1] = (((uint64_t) x4) << 32) | x5;
|
||||
r1->d[0] = (((uint64_t) x6) << 32) | x7;
|
||||
r2->d[3] = (((uint64_t) x8) << 32) | x9;
|
||||
r2->d[2] = (((uint64_t) x10) << 32) | x11;
|
||||
r2->d[1] = (((uint64_t) x12) << 32) | x13;
|
||||
r2->d[0] = (((uint64_t) x14) << 32) | x15;
|
||||
|
||||
over1 = secp256k1_scalar_check_overflow(r1);
|
||||
over2 = secp256k1_scalar_check_overflow(r2);
|
||||
over_count++;
|
||||
} while (over1 | over2);
|
||||
}
|
||||
|
||||
#undef ROTL32
|
||||
#undef QUARTERROUND
|
||||
#undef LE32
|
||||
|
||||
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef SECP256K1_SCALAR_REPR_IMPL_H
|
||||
#define SECP256K1_SCALAR_REPR_IMPL_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Limbs of the secp256k1 order. */
|
||||
#define SECP256K1_N_0 ((uint32_t)0xD0364141UL)
|
||||
#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL)
|
||||
|
|
@ -282,9 +284,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
c0 += tl; /* overflow is handled on the next line */ \
|
||||
th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
|
||||
th += (c0 < tl); /* at most 0xFFFFFFFF */ \
|
||||
c1 += th; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
|
||||
c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
|
||||
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
|
||||
}
|
||||
|
||||
|
|
@ -297,7 +299,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
c0 += tl; /* overflow is handled on the next line */ \
|
||||
th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
|
||||
th += (c0 < tl); /* at most 0xFFFFFFFF */ \
|
||||
c1 += th; /* never overflows by contract (verified in the next line) */ \
|
||||
VERIFY_CHECK(c1 >= th); \
|
||||
}
|
||||
|
|
@ -311,16 +313,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
tl = t; \
|
||||
} \
|
||||
th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \
|
||||
c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
|
||||
tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \
|
||||
th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
|
||||
th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \
|
||||
c0 += tl2; /* overflow is handled on the next line */ \
|
||||
th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
|
||||
th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
|
||||
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
|
||||
c1 += th2; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
|
||||
}
|
||||
|
||||
|
|
@ -328,15 +330,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
|||
#define sumadd(a) { \
|
||||
unsigned int over; \
|
||||
c0 += (a); /* overflow is handled on the next line */ \
|
||||
over = (c0 < (a)) ? 1 : 0; \
|
||||
over = (c0 < (a)); \
|
||||
c1 += over; /* overflow is handled on the next line */ \
|
||||
c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
|
||||
c2 += (c1 < over); /* never overflows by contract */ \
|
||||
}
|
||||
|
||||
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
|
||||
#define sumadd_fast(a) { \
|
||||
c0 += (a); /* overflow is handled on the next line */ \
|
||||
c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
|
||||
c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
|
||||
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
|
||||
VERIFY_CHECK(c2 == 0); \
|
||||
}
|
||||
|
|
@ -683,26 +685,24 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
|
|||
secp256k1_scalar_reduce_512(r, l);
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
r1->d[0] = a->d[0];
|
||||
r1->d[1] = a->d[1];
|
||||
r1->d[2] = a->d[2];
|
||||
r1->d[3] = a->d[3];
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
|
||||
r1->d[0] = k->d[0];
|
||||
r1->d[1] = k->d[1];
|
||||
r1->d[2] = k->d[2];
|
||||
r1->d[3] = k->d[3];
|
||||
r1->d[4] = 0;
|
||||
r1->d[5] = 0;
|
||||
r1->d[6] = 0;
|
||||
r1->d[7] = 0;
|
||||
r2->d[0] = a->d[4];
|
||||
r2->d[1] = a->d[5];
|
||||
r2->d[2] = a->d[6];
|
||||
r2->d[3] = a->d[7];
|
||||
r2->d[0] = k->d[4];
|
||||
r2->d[1] = k->d[5];
|
||||
r2->d[2] = k->d[6];
|
||||
r2->d[3] = k->d[7];
|
||||
r2->d[4] = 0;
|
||||
r2->d[5] = 0;
|
||||
r2->d[6] = 0;
|
||||
r2->d[7] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0;
|
||||
|
|
@ -729,4 +729,114 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
|
|||
secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1);
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
|
||||
uint32_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r->d, sizeof(r->d));
|
||||
mask0 = flag + ~((uint32_t)0);
|
||||
mask1 = ~mask0;
|
||||
r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
|
||||
r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1);
|
||||
r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1);
|
||||
r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1);
|
||||
r->d[4] = (r->d[4] & mask0) | (a->d[4] & mask1);
|
||||
r->d[5] = (r->d[5] & mask0) | (a->d[5] & mask1);
|
||||
r->d[6] = (r->d[6] & mask0) | (a->d[6] & mask1);
|
||||
r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1);
|
||||
}
|
||||
|
||||
#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n)))
|
||||
#define QUARTERROUND(a,b,c,d) \
|
||||
a += b; d = ROTL32(d ^ a, 16); \
|
||||
c += d; b = ROTL32(b ^ c, 12); \
|
||||
a += b; d = ROTL32(d ^ a, 8); \
|
||||
c += d; b = ROTL32(b ^ c, 7);
|
||||
|
||||
#if defined(SECP256K1_BIG_ENDIAN)
|
||||
#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
|
||||
#elif defined(SECP256K1_LITTLE_ENDIAN)
|
||||
#define LE32(p) (p)
|
||||
#endif
|
||||
|
||||
static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) {
|
||||
size_t n;
|
||||
size_t over_count = 0;
|
||||
uint32_t seed32[8];
|
||||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
int over1, over2;
|
||||
|
||||
memcpy((void *) seed32, (const void *) seed, 32);
|
||||
do {
|
||||
x0 = 0x61707865;
|
||||
x1 = 0x3320646e;
|
||||
x2 = 0x79622d32;
|
||||
x3 = 0x6b206574;
|
||||
x4 = LE32(seed32[0]);
|
||||
x5 = LE32(seed32[1]);
|
||||
x6 = LE32(seed32[2]);
|
||||
x7 = LE32(seed32[3]);
|
||||
x8 = LE32(seed32[4]);
|
||||
x9 = LE32(seed32[5]);
|
||||
x10 = LE32(seed32[6]);
|
||||
x11 = LE32(seed32[7]);
|
||||
x12 = idx;
|
||||
x13 = idx >> 32;
|
||||
x14 = 0;
|
||||
x15 = over_count;
|
||||
|
||||
n = 10;
|
||||
while (n--) {
|
||||
QUARTERROUND(x0, x4, x8,x12)
|
||||
QUARTERROUND(x1, x5, x9,x13)
|
||||
QUARTERROUND(x2, x6,x10,x14)
|
||||
QUARTERROUND(x3, x7,x11,x15)
|
||||
QUARTERROUND(x0, x5,x10,x15)
|
||||
QUARTERROUND(x1, x6,x11,x12)
|
||||
QUARTERROUND(x2, x7, x8,x13)
|
||||
QUARTERROUND(x3, x4, x9,x14)
|
||||
}
|
||||
|
||||
x0 += 0x61707865;
|
||||
x1 += 0x3320646e;
|
||||
x2 += 0x79622d32;
|
||||
x3 += 0x6b206574;
|
||||
x4 += LE32(seed32[0]);
|
||||
x5 += LE32(seed32[1]);
|
||||
x6 += LE32(seed32[2]);
|
||||
x7 += LE32(seed32[3]);
|
||||
x8 += LE32(seed32[4]);
|
||||
x9 += LE32(seed32[5]);
|
||||
x10 += LE32(seed32[6]);
|
||||
x11 += LE32(seed32[7]);
|
||||
x12 += idx;
|
||||
x13 += idx >> 32;
|
||||
x14 += 0;
|
||||
x15 += over_count;
|
||||
|
||||
r1->d[7] = x0;
|
||||
r1->d[6] = x1;
|
||||
r1->d[5] = x2;
|
||||
r1->d[4] = x3;
|
||||
r1->d[3] = x4;
|
||||
r1->d[2] = x5;
|
||||
r1->d[1] = x6;
|
||||
r1->d[0] = x7;
|
||||
r2->d[7] = x8;
|
||||
r2->d[6] = x9;
|
||||
r2->d[5] = x10;
|
||||
r2->d[4] = x11;
|
||||
r2->d[3] = x12;
|
||||
r2->d[2] = x13;
|
||||
r2->d[1] = x14;
|
||||
r2->d[0] = x15;
|
||||
|
||||
over1 = secp256k1_scalar_check_overflow(r1);
|
||||
over2 = secp256k1_scalar_check_overflow(r2);
|
||||
over_count++;
|
||||
} while (over1 | over2);
|
||||
}
|
||||
|
||||
#undef ROTL32
|
||||
#undef QUARTERROUND
|
||||
#undef LE32
|
||||
|
||||
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@
|
|||
#ifndef SECP256K1_SCALAR_IMPL_H
|
||||
#define SECP256K1_SCALAR_IMPL_H
|
||||
|
||||
#include "group.h"
|
||||
#ifdef VERIFY
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "scalar.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined HAVE_CONFIG_H
|
||||
#include "libsecp256k1-config.h"
|
||||
|
|
@ -16,14 +20,17 @@
|
|||
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
#include "scalar_low_impl.h"
|
||||
#elif defined(USE_SCALAR_4X64)
|
||||
#elif defined(SECP256K1_WIDEMUL_INT128)
|
||||
#include "scalar_4x64_impl.h"
|
||||
#elif defined(USE_SCALAR_8X32)
|
||||
#elif defined(SECP256K1_WIDEMUL_INT64)
|
||||
#include "scalar_8x32_impl.h"
|
||||
#else
|
||||
#error "Please select scalar implementation"
|
||||
#error "Please select wide multiplication implementation"
|
||||
#endif
|
||||
|
||||
static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
|
||||
static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
#ifndef USE_NUM_NONE
|
||||
static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) {
|
||||
unsigned char c[32];
|
||||
|
|
@ -52,6 +59,12 @@ static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) {
|
||||
int overflow;
|
||||
secp256k1_scalar_set_b32(r, bin, &overflow);
|
||||
return (!overflow) & (!secp256k1_scalar_is_zero(r));
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
int i;
|
||||
|
|
@ -243,37 +256,65 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
# if EXHAUSTIVE_TEST_ORDER == 13
|
||||
# define EXHAUSTIVE_TEST_LAMBDA 9
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 199
|
||||
# define EXHAUSTIVE_TEST_LAMBDA 92
|
||||
# else
|
||||
# error No known lambda for the specified exhaustive test group order.
|
||||
# endif
|
||||
|
||||
/**
|
||||
* Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
|
||||
* full case we don't bother making k1 and k2 be small, we just want them to be
|
||||
* Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the
|
||||
* full case we don't bother making r1 and r2 be small, we just want them to be
|
||||
* nontrivial to get full test coverage for the exhaustive tests. We therefore
|
||||
* (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
|
||||
* (arbitrarily) set r2 = k + 5 (mod n) and r1 = k - r2 * lambda (mod n).
|
||||
*/
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
*r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
|
||||
*r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
|
||||
*r2 = (*k + 5) % EXHAUSTIVE_TEST_ORDER;
|
||||
*r1 = (*k + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
|
||||
* lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
|
||||
* 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72}
|
||||
* lambda is: */
|
||||
static const secp256k1_scalar secp256k1_const_lambda = SECP256K1_SCALAR_CONST(
|
||||
0x5363AD4CUL, 0xC05C30E0UL, 0xA5261C02UL, 0x8812645AUL,
|
||||
0x122E22EAUL, 0x20816678UL, 0xDF02967CUL, 0x1B23BD72UL
|
||||
);
|
||||
|
||||
#ifdef VERIFY
|
||||
static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and
|
||||
* beta^3 == 1 mod p, where n is the curve order and p is the field order.
|
||||
*
|
||||
* "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
|
||||
* (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
|
||||
* and k2 have a small size.
|
||||
* It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are:
|
||||
* Futhermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are
|
||||
* roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p.
|
||||
* (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.)
|
||||
*
|
||||
* Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring
|
||||
* homomorphism phi : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n. The kernel of phi
|
||||
* is a lattice over Z[l] (considering Z[l] as a Z-module). This lattice is generated by a
|
||||
* reduced basis {a1 + b1*l, a2 + b2*l} where
|
||||
*
|
||||
* - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
|
||||
* - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3}
|
||||
* - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8}
|
||||
* - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15}
|
||||
*
|
||||
* The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives
|
||||
* "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm
|
||||
* (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1
|
||||
* and k2 are small in absolute value.
|
||||
*
|
||||
* The algorithm computes c1 = round(b2 * k / n) and c2 = round((-b1) * k / n), and gives
|
||||
* k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and
|
||||
* compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2.
|
||||
* compute r2 = k2 mod n, and r1 = k1 mod n = (k - r2 * lambda) mod n, avoiding the need for
|
||||
* the constants a1 and a2.
|
||||
*
|
||||
* g1, g2 are precomputed constants used to replace division with a rounded multiplication
|
||||
* when decomposing the scalar for an endomorphism-based point multiplication.
|
||||
|
|
@ -285,21 +326,21 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
|
|||
* Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez),
|
||||
* Section 4.3 (here we use a somewhat higher-precision estimate):
|
||||
* d = a1*b2 - b1*a2
|
||||
* g1 = round((2^272)*b2/d)
|
||||
* g2 = round((2^272)*b1/d)
|
||||
* g1 = round(2^384 * b2/d)
|
||||
* g2 = round(2^384 * (-b1)/d)
|
||||
*
|
||||
* (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found
|
||||
* as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda').
|
||||
* (Note that d is also equal to the curve order, n, here because [a1,b1] and [a2,b2]
|
||||
* can be found as outputs of the Extended Euclidean Algorithm on inputs n and lambda).
|
||||
*
|
||||
* The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order).
|
||||
* The function below splits k into r1 and r2, such that
|
||||
* - r1 + lambda * r2 == k (mod n)
|
||||
* - either r1 < 2^128 or -r1 mod n < 2^128
|
||||
* - either r2 < 2^128 or -r2 mod n < 2^128
|
||||
*
|
||||
* See proof below.
|
||||
*/
|
||||
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) {
|
||||
secp256k1_scalar c1, c2;
|
||||
static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST(
|
||||
0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL,
|
||||
0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL
|
||||
);
|
||||
static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST(
|
||||
0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL,
|
||||
0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL
|
||||
|
|
@ -309,25 +350,167 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
|
|||
0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL
|
||||
);
|
||||
static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST(
|
||||
0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL,
|
||||
0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL
|
||||
0x3086D221UL, 0xA7D46BCDUL, 0xE86C90E4UL, 0x9284EB15UL,
|
||||
0x3DAA8A14UL, 0x71E8CA7FUL, 0xE893209AUL, 0x45DBB031UL
|
||||
);
|
||||
static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST(
|
||||
0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL,
|
||||
0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL
|
||||
0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C4UL,
|
||||
0x221208ACUL, 0x9DF506C6UL, 0x1571B4AEUL, 0x8AC47F71UL
|
||||
);
|
||||
VERIFY_CHECK(r1 != a);
|
||||
VERIFY_CHECK(r2 != a);
|
||||
VERIFY_CHECK(r1 != k);
|
||||
VERIFY_CHECK(r2 != k);
|
||||
/* these _var calls are constant time since the shift amount is constant */
|
||||
secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272);
|
||||
secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272);
|
||||
secp256k1_scalar_mul_shift_var(&c1, k, &g1, 384);
|
||||
secp256k1_scalar_mul_shift_var(&c2, k, &g2, 384);
|
||||
secp256k1_scalar_mul(&c1, &c1, &minus_b1);
|
||||
secp256k1_scalar_mul(&c2, &c2, &minus_b2);
|
||||
secp256k1_scalar_add(r2, &c1, &c2);
|
||||
secp256k1_scalar_mul(r1, r2, &minus_lambda);
|
||||
secp256k1_scalar_add(r1, r1, a);
|
||||
secp256k1_scalar_mul(r1, r2, &secp256k1_const_lambda);
|
||||
secp256k1_scalar_negate(r1, r1);
|
||||
secp256k1_scalar_add(r1, r1, k);
|
||||
|
||||
#ifdef VERIFY
|
||||
secp256k1_scalar_split_lambda_verify(r1, r2, k);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef VERIFY
|
||||
/*
|
||||
* Proof for secp256k1_scalar_split_lambda's bounds.
|
||||
*
|
||||
* Let
|
||||
* - epsilon1 = 2^256 * |g1/2^384 - b2/d|
|
||||
* - epsilon2 = 2^256 * |g2/2^384 - (-b1)/d|
|
||||
* - c1 = round(k*g1/2^384)
|
||||
* - c2 = round(k*g2/2^384)
|
||||
*
|
||||
* Lemma 1: |c1 - k*b2/d| < 2^-1 + epsilon1
|
||||
*
|
||||
* |c1 - k*b2/d|
|
||||
* =
|
||||
* |c1 - k*g1/2^384 + k*g1/2^384 - k*b2/d|
|
||||
* <= {triangle inequality}
|
||||
* |c1 - k*g1/2^384| + |k*g1/2^384 - k*b2/d|
|
||||
* =
|
||||
* |c1 - k*g1/2^384| + k*|g1/2^384 - b2/d|
|
||||
* < {rounding in c1 and 0 <= k < 2^256}
|
||||
* 2^-1 + 2^256 * |g1/2^384 - b2/d|
|
||||
* = {definition of epsilon1}
|
||||
* 2^-1 + epsilon1
|
||||
*
|
||||
* Lemma 2: |c2 - k*(-b1)/d| < 2^-1 + epsilon2
|
||||
*
|
||||
* |c2 - k*(-b1)/d|
|
||||
* =
|
||||
* |c2 - k*g2/2^384 + k*g2/2^384 - k*(-b1)/d|
|
||||
* <= {triangle inequality}
|
||||
* |c2 - k*g2/2^384| + |k*g2/2^384 - k*(-b1)/d|
|
||||
* =
|
||||
* |c2 - k*g2/2^384| + k*|g2/2^384 - (-b1)/d|
|
||||
* < {rounding in c2 and 0 <= k < 2^256}
|
||||
* 2^-1 + 2^256 * |g2/2^384 - (-b1)/d|
|
||||
* = {definition of epsilon2}
|
||||
* 2^-1 + epsilon2
|
||||
*
|
||||
* Let
|
||||
* - k1 = k - c1*a1 - c2*a2
|
||||
* - k2 = - c1*b1 - c2*b2
|
||||
*
|
||||
* Lemma 3: |k1| < (a1 + a2 + 1)/2 < 2^128
|
||||
*
|
||||
* |k1|
|
||||
* = {definition of k1}
|
||||
* |k - c1*a1 - c2*a2|
|
||||
* = {(a1*b2 - b1*a2)/n = 1}
|
||||
* |k*(a1*b2 - b1*a2)/n - c1*a1 - c2*a2|
|
||||
* =
|
||||
* |a1*(k*b2/n - c1) + a2*(k*(-b1)/n - c2)|
|
||||
* <= {triangle inequality}
|
||||
* a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2|
|
||||
* < {Lemma 1 and Lemma 2}
|
||||
* a1*(2^-1 + epslion1) + a2*(2^-1 + epsilon2)
|
||||
* < {rounding up to an integer}
|
||||
* (a1 + a2 + 1)/2
|
||||
* < {rounding up to a power of 2}
|
||||
* 2^128
|
||||
*
|
||||
* Lemma 4: |k2| < (-b1 + b2)/2 + 1 < 2^128
|
||||
*
|
||||
* |k2|
|
||||
* = {definition of k2}
|
||||
* |- c1*a1 - c2*a2|
|
||||
* = {(b1*b2 - b1*b2)/n = 0}
|
||||
* |k*(b1*b2 - b1*b2)/n - c1*b1 - c2*b2|
|
||||
* =
|
||||
* |b1*(k*b2/n - c1) + b2*(k*(-b1)/n - c2)|
|
||||
* <= {triangle inequality}
|
||||
* (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2|
|
||||
* < {Lemma 1 and Lemma 2}
|
||||
* (-b1)*(2^-1 + epslion1) + b2*(2^-1 + epsilon2)
|
||||
* < {rounding up to an integer}
|
||||
* (-b1 + b2)/2 + 1
|
||||
* < {rounding up to a power of 2}
|
||||
* 2^128
|
||||
*
|
||||
* Let
|
||||
* - r2 = k2 mod n
|
||||
* - r1 = k - r2*lambda mod n.
|
||||
*
|
||||
* Notice that r1 is defined such that r1 + r2 * lambda == k (mod n).
|
||||
*
|
||||
* Lemma 5: r1 == k1 mod n.
|
||||
*
|
||||
* r1
|
||||
* == {definition of r1 and r2}
|
||||
* k - k2*lambda
|
||||
* == {definition of k2}
|
||||
* k - (- c1*b1 - c2*b2)*lambda
|
||||
* ==
|
||||
* k + c1*b1*lambda + c2*b2*lambda
|
||||
* == {a1 + b1*lambda == 0 mod n and a2 + b2*lambda == 0 mod n}
|
||||
* k - c1*a1 - c2*a2
|
||||
* == {definition of k1}
|
||||
* k1
|
||||
*
|
||||
* From Lemma 3, Lemma 4, Lemma 5 and the definition of r2, we can conclude that
|
||||
*
|
||||
* - either r1 < 2^128 or -r1 mod n < 2^128
|
||||
* - either r2 < 2^128 or -r2 mod n < 2^128.
|
||||
*
|
||||
* Q.E.D.
|
||||
*/
|
||||
static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k) {
|
||||
secp256k1_scalar s;
|
||||
unsigned char buf1[32];
|
||||
unsigned char buf2[32];
|
||||
|
||||
/* (a1 + a2 + 1)/2 is 0xa2a8918ca85bafe22016d0b917e4dd77 */
|
||||
static const unsigned char k1_bound[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xa2, 0xa8, 0x91, 0x8c, 0xa8, 0x5b, 0xaf, 0xe2, 0x20, 0x16, 0xd0, 0xb9, 0x17, 0xe4, 0xdd, 0x77
|
||||
};
|
||||
|
||||
/* (-b1 + b2)/2 + 1 is 0x8a65287bd47179fb2be08846cea267ed */
|
||||
static const unsigned char k2_bound[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x8a, 0x65, 0x28, 0x7b, 0xd4, 0x71, 0x79, 0xfb, 0x2b, 0xe0, 0x88, 0x46, 0xce, 0xa2, 0x67, 0xed
|
||||
};
|
||||
|
||||
secp256k1_scalar_mul(&s, &secp256k1_const_lambda, r2);
|
||||
secp256k1_scalar_add(&s, &s, r1);
|
||||
VERIFY_CHECK(secp256k1_scalar_eq(&s, k));
|
||||
|
||||
secp256k1_scalar_negate(&s, r1);
|
||||
secp256k1_scalar_get_b32(buf1, r1);
|
||||
secp256k1_scalar_get_b32(buf2, &s);
|
||||
VERIFY_CHECK(secp256k1_memcmp_var(buf1, k1_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k1_bound, 32) < 0);
|
||||
|
||||
secp256k1_scalar_negate(&s, r2);
|
||||
secp256k1_scalar_get_b32(buf1, r2);
|
||||
secp256k1_scalar_get_b32(buf2, &s);
|
||||
VERIFY_CHECK(secp256k1_memcmp_var(buf1, k2_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k2_bound, 32) < 0);
|
||||
}
|
||||
#endif /* VERIFY */
|
||||
#endif /* !defined(EXHAUSTIVE_TEST_ORDER) */
|
||||
|
||||
#endif /* SECP256K1_SCALAR_IMPL_H */
|
||||
|
|
|
|||
|
|
@ -12,4 +12,6 @@
|
|||
/** A scalar modulo the group order of the secp256k1 curve. */
|
||||
typedef uint32_t secp256k1_scalar;
|
||||
|
||||
#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) (d0)
|
||||
|
||||
#endif /* SECP256K1_SCALAR_REPR_H */
|
||||
|
|
|
|||
|
|
@ -39,21 +39,27 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a,
|
|||
|
||||
static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
|
||||
if (flag && bit < 32)
|
||||
*r += (1 << bit);
|
||||
*r += ((uint32_t)1 << bit);
|
||||
#ifdef VERIFY
|
||||
VERIFY_CHECK(bit < 32);
|
||||
/* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */
|
||||
VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER);
|
||||
VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
|
||||
const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
|
||||
int i;
|
||||
int over = 0;
|
||||
*r = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
*r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
|
||||
*r = (*r * 0x100) + b32[i];
|
||||
if (*r >= EXHAUSTIVE_TEST_ORDER) {
|
||||
over = 1;
|
||||
*r %= EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
}
|
||||
/* just deny overflow, it basically always happens */
|
||||
if (overflow) *overflow = 0;
|
||||
if (overflow) *overflow = over;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
|
||||
|
|
@ -112,4 +118,17 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const
|
|||
return *a == *b;
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
|
||||
uint32_t mask0, mask1;
|
||||
VG_CHECK_VERIFY(r, sizeof(*r));
|
||||
mask0 = flag + ~((uint32_t)0);
|
||||
mask1 = ~mask0;
|
||||
*r = (*r & mask0) | (*a & mask1);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) {
|
||||
*r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER;
|
||||
*r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_SCALAR_REPR_IMPL_H */
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue